summaryrefslogtreecommitdiff
path: root/searx/__init__.py
blob: 045affab05c65758e4bc982e833b73e62fffb625 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# SPDX-License-Identifier: AGPL-3.0-or-later
# pylint: disable=missing-module-docstring, cyclic-import
from __future__ import annotations

import typing as t
import sys
import os
from os.path import dirname, abspath

import logging

import searx.unixthreadname  # pylint: disable=unused-import

# Debug
LOG_FORMAT_DEBUG: str = '%(levelname)-7s %(name)-30.30s: %(message)s'

# Production
LOG_FORMAT_PROD: str = '%(asctime)-15s %(levelname)s:%(name)s: %(message)s'
LOG_LEVEL_PROD = logging.WARNING

searx_dir: str = abspath(dirname(__file__))
searx_parent_dir: str = abspath(dirname(dirname(__file__)))

settings: dict[str, t.Any] = {}

sxng_debug: bool = False
logger = logging.getLogger('searx')

_unset = object()


def init_settings():
    """Initialize global ``settings`` and ``sxng_debug`` variables and
    ``logger`` from ``SEARXNG_SETTINGS_PATH``.
    """

    # pylint: disable=import-outside-toplevel
    from searx import settings_loader
    from searx.settings_defaults import SCHEMA, apply_schema

    global settings, sxng_debug  # pylint: disable=global-variable-not-assigned

    cfg, msg = settings_loader.load_settings(load_user_settings=True)
    cfg = cfg or {}
    apply_schema(cfg, SCHEMA, [])

    settings.clear()
    settings.update(cfg)

    sxng_debug = get_setting("general.debug")
    if sxng_debug:
        _logging_config_debug()
    else:
        logging.basicConfig(level=LOG_LEVEL_PROD, format=LOG_FORMAT_PROD)
        logging.root.setLevel(level=LOG_LEVEL_PROD)
        logging.getLogger('werkzeug').setLevel(level=LOG_LEVEL_PROD)
        logger.info(msg)

    # log max_request_timeout
    max_request_timeout: int | None = settings['outgoing']['max_request_timeout']
    if max_request_timeout is None:
        logger.info('max_request_timeout=%s', repr(max_request_timeout))
    else:
        logger.info('max_request_timeout=%i second(s)', max_request_timeout)

    if settings['server']['public_instance']:
        logger.warning(
            "Be aware you have activated features intended only for public instances. "
            "This force the usage of the limiter and link_token / "
            "see https://docs.searxng.org/admin/searx.limiter.html"
        )


def get_setting(name: str, default: t.Any = _unset) -> t.Any:
    """Returns the value to which ``name`` point.  If there is no such name in the
    settings and the ``default`` is unset, a :py:obj:`KeyError` is raised.

    """
    value: dict[str, t.Any] = settings
    for a in name.split('.'):
        if isinstance(value, dict):
            value = value.get(a, _unset)
        else:
            value = _unset  # type: ignore

        if value is _unset:
            if default is _unset:
                raise KeyError(name)
            value = default  # type: ignore
            break

    return value


def _is_color_terminal():
    if os.getenv('TERM') in ('dumb', 'unknown'):
        return False
    return sys.stdout.isatty()


def _logging_config_debug():
    try:
        import coloredlogs  # pylint: disable=import-outside-toplevel
    except ImportError:
        coloredlogs = None

    log_level = os.environ.get('SEARXNG_DEBUG_LOG_LEVEL', 'DEBUG')
    if coloredlogs and _is_color_terminal():
        level_styles = {
            'spam': {'color': 'green', 'faint': True},
            'debug': {},
            'notice': {'color': 'magenta'},
            'success': {'bold': True, 'color': 'green'},
            'info': {'bold': True, 'color': 'cyan'},
            'warning': {'color': 'yellow'},
            'error': {'color': 'red'},
            'critical': {'bold': True, 'color': 'red'},
        }
        field_styles = {
            'asctime': {'color': 'green'},
            'hostname': {'color': 'magenta'},
            'levelname': {'color': 8},
            'name': {'color': 8},
            'programname': {'color': 'cyan'},
            'username': {'color': 'yellow'},
        }
        coloredlogs.install(  # type: ignore
            level=log_level,
            level_styles=level_styles,
            field_styles=field_styles,
            fmt=LOG_FORMAT_DEBUG,
        )
    else:
        logging.basicConfig(level=getattr(logging, log_level, "ERROR"), format=LOG_FORMAT_DEBUG)


init_settings()