codekingpro/portable-devtools
114k
1# Copyright The OpenTelemetry Authors2#3# Licensed under the Apache License, Version 2.0 (the "License");4# you may not use this file except in compliance with the License.5# You may obtain a copy of the License at6#7# http://www.apache.org/licenses/LICENSE-2.08#9# Unless required by applicable law or agreed to in writing, software10# distributed under the License is distributed on an "AS IS" BASIS,11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.12# See the License for the specific language governing permissions and13# limitations under the License.14"""15The OpenTelemetry logging API describes the classes used to generate logs and events.16 17The :class:`.LoggerProvider` provides users access to the :class:`.Logger`.18 19This module provides abstract (i.e. unimplemented) classes required for20logging, and a concrete no-op implementation :class:`.NoOpLogger` that allows applications21to use the API package alone without a supporting implementation.22 23To get a logger, you need to provide the package name from which you are24calling the logging APIs to OpenTelemetry by calling `LoggerProvider.get_logger`25with the calling module name and the version of your package.26 27The following code shows how to obtain a logger using the global :class:`.LoggerProvider`::28 29 from opentelemetry._logs import get_logger30 31 logger = get_logger("example-logger")32 33.. versionadded:: 1.15.034"""35 36from __future__ import annotations37 38from abc import ABC, abstractmethod39from logging import getLogger40from os import environ41from time import time_ns42from typing import Optional, cast, overload43 44from typing_extensions import deprecated45 46from opentelemetry._logs.severity import SeverityNumber47from opentelemetry.context import get_current48from opentelemetry.context.context import Context49from opentelemetry.environment_variables import _OTEL_PYTHON_LOGGER_PROVIDER50from opentelemetry.trace import get_current_span51from opentelemetry.trace.span import TraceFlags52from opentelemetry.util._once import Once53from opentelemetry.util._providers import _load_provider54from opentelemetry.util.types import AnyValue, _ExtendedAttributes55 56_logger = getLogger(__name__)57 58 59class LogRecord(ABC):60 """A LogRecord instance represents an event being logged.61 62 LogRecord instances are created and emitted via `Logger`63 every time something is logged. They contain all the information64 pertinent to the event being logged.65 """66 67 @overload68 def __init__(69 self,70 *,71 timestamp: Optional[int] = None,72 observed_timestamp: Optional[int] = None,73 context: Optional[Context] = None,74 severity_text: Optional[str] = None,75 severity_number: Optional[SeverityNumber] = None,76 body: AnyValue = None,77 attributes: Optional[_ExtendedAttributes] = None,78 event_name: Optional[str] = None,79 ) -> None: ...80 81 @overload82 @deprecated(83 "LogRecord init with `trace_id`, `span_id`, and/or `trace_flags` is deprecated since 1.35.0. Use `context` instead."84 )85 def __init__(86 self,87 *,88 timestamp: Optional[int] = None,89 observed_timestamp: Optional[int] = None,90 trace_id: Optional[int] = None,91 span_id: Optional[int] = None,92 trace_flags: Optional[TraceFlags] = None,93 severity_text: Optional[str] = None,94 severity_number: Optional[SeverityNumber] = None,95 body: AnyValue = None,96 attributes: Optional[_ExtendedAttributes] = None,97 ) -> None: ...98 99 def __init__(100 self,101 *,102 timestamp: Optional[int] = None,103 observed_timestamp: Optional[int] = None,104 context: Optional[Context] = None,105 trace_id: Optional[int] = None,106 span_id: Optional[int] = None,107 trace_flags: Optional[TraceFlags] = None,108 severity_text: Optional[str] = None,109 severity_number: Optional[SeverityNumber] = None,110 body: AnyValue = None,111 attributes: Optional[_ExtendedAttributes] = None,112 event_name: Optional[str] = None,113 ) -> None:114 if not context:115 context = get_current()116 span_context = get_current_span(context).get_span_context()117 self.timestamp = timestamp118 if observed_timestamp is None:119 observed_timestamp = time_ns()120 self.observed_timestamp = observed_timestamp121 self.context = context122 self.trace_id = trace_id or span_context.trace_id123 self.span_id = span_id or span_context.span_id124 self.trace_flags = trace_flags or span_context.trace_flags125 self.severity_text = severity_text126 self.severity_number = severity_number127 self.body = body128 self.attributes = attributes129 self.event_name = event_name130 131 132class Logger(ABC):133 """Handles emitting events and logs via `LogRecord`."""134 135 def __init__(136 self,137 name: str,138 version: Optional[str] = None,139 schema_url: Optional[str] = None,140 attributes: Optional[_ExtendedAttributes] = None,141 ) -> None:142 super().__init__()143 self._name = name144 self._version = version145 self._schema_url = schema_url146 self._attributes = attributes147 148 @overload149 def emit(150 self,151 *,152 timestamp: int | None = None,153 observed_timestamp: int | None = None,154 context: Context | None = None,155 severity_number: SeverityNumber | None = None,156 severity_text: str | None = None,157 body: AnyValue | None = None,158 attributes: _ExtendedAttributes | None = None,159 event_name: str | None = None,160 ) -> None: ...161 162 @overload163 def emit(164 self,165 record: LogRecord,166 ) -> None: ...167 168 @abstractmethod169 def emit(170 self,171 record: LogRecord | None = None,172 *,173 timestamp: int | None = None,174 observed_timestamp: int | None = None,175 context: Context | None = None,176 severity_number: SeverityNumber | None = None,177 severity_text: str | None = None,178 body: AnyValue | None = None,179 attributes: _ExtendedAttributes | None = None,180 event_name: str | None = None,181 ) -> None:182 """Emits a :class:`LogRecord` representing a log to the processing pipeline."""183 184 185class NoOpLogger(Logger):186 """The default Logger used when no Logger implementation is available.187 188 All operations are no-op.189 """190 191 @overload192 def emit(193 self,194 *,195 timestamp: int | None = None,196 observed_timestamp: int | None = None,197 context: Context | None = None,198 severity_number: SeverityNumber | None = None,199 severity_text: str | None = None,200 body: AnyValue | None = None,201 attributes: _ExtendedAttributes | None = None,202 event_name: str | None = None,203 ) -> None: ...204 205 @overload206 def emit( # pylint:disable=arguments-differ207 self,208 record: LogRecord,209 ) -> None: ...210 211 def emit(212 self,213 record: LogRecord | None = None,214 *,215 timestamp: int | None = None,216 observed_timestamp: int | None = None,217 context: Context | None = None,218 severity_number: SeverityNumber | None = None,219 severity_text: str | None = None,220 body: AnyValue | None = None,221 attributes: _ExtendedAttributes | None = None,222 event_name: str | None = None,223 ) -> None:224 pass225 226 227class ProxyLogger(Logger):228 def __init__( # pylint: disable=super-init-not-called229 self,230 name: str,231 version: Optional[str] = None,232 schema_url: Optional[str] = None,233 attributes: Optional[_ExtendedAttributes] = None,234 ):235 self._name = name236 self._version = version237 self._schema_url = schema_url238 self._attributes = attributes239 self._real_logger: Optional[Logger] = None240 self._noop_logger = NoOpLogger(name)241 242 @property243 def _logger(self) -> Logger:244 if self._real_logger:245 return self._real_logger246 247 if _LOGGER_PROVIDER:248 self._real_logger = _LOGGER_PROVIDER.get_logger(249 self._name,250 self._version,251 self._schema_url,252 self._attributes,253 )254 return self._real_logger255 return self._noop_logger256 257 @overload258 def emit(259 self,260 *,261 timestamp: int | None = None,262 observed_timestamp: int | None = None,263 context: Context | None = None,264 severity_number: SeverityNumber | None = None,265 severity_text: str | None = None,266 body: AnyValue | None = None,267 attributes: _ExtendedAttributes | None = None,268 event_name: str | None = None,269 ) -> None: ...270 271 @overload272 def emit( # pylint:disable=arguments-differ273 self,274 record: LogRecord,275 ) -> None: ...276 277 def emit(278 self,279 record: LogRecord | None = None,280 *,281 timestamp: int | None = None,282 observed_timestamp: int | None = None,283 context: Context | None = None,284 severity_number: SeverityNumber | None = None,285 severity_text: str | None = None,286 body: AnyValue | None = None,287 attributes: _ExtendedAttributes | None = None,288 event_name: str | None = None,289 ) -> None:290 if record:291 self._logger.emit(record)292 else:293 self._logger.emit(294 timestamp=timestamp,295 observed_timestamp=observed_timestamp,296 context=context,297 severity_number=severity_number,298 severity_text=severity_text,299 body=body,300 attributes=attributes,301 event_name=event_name,302 )303 304 305class LoggerProvider(ABC):306 """307 LoggerProvider is the entry point of the API. It provides access to Logger instances.308 """309 310 @abstractmethod311 def get_logger(312 self,313 name: str,314 version: Optional[str] = None,315 schema_url: Optional[str] = None,316 attributes: Optional[_ExtendedAttributes] = None,317 ) -> Logger:318 """Returns a `Logger` for use by the given instrumentation library.319 320 For any two calls with identical parameters, it is undefined whether the same321 or different `Logger` instances are returned.322 323 This function may return different `Logger` types (e.g. a no-op logger324 vs. a functional logger).325 326 Args:327 name: The name of the instrumenting module, package or class.328 This should *not* be the name of the module, package or class that is329 instrumented but the name of the code doing the instrumentation.330 E.g., instead of ``"requests"``, use331 ``"opentelemetry.instrumentation.requests"``.332 333 For log sources which define a logger name (e.g. logging.Logger.name)334 the Logger Name should be recorded as the instrumentation scope name.335 336 version: Optional. The version string of the337 instrumenting library. Usually this should be the same as338 ``importlib.metadata.version(instrumenting_library_name)``.339 340 schema_url: Optional. Specifies the Schema URL of the emitted telemetry.341 342 attributes: Optional. Specifies the instrumentation scope attributes to343 associate with emitted telemetry.344 """345 346 347class NoOpLoggerProvider(LoggerProvider):348 """The default LoggerProvider used when no LoggerProvider implementation is available."""349 350 def get_logger(351 self,352 name: str,353 version: Optional[str] = None,354 schema_url: Optional[str] = None,355 attributes: Optional[_ExtendedAttributes] = None,356 ) -> Logger:357 """Returns a NoOpLogger."""358 return NoOpLogger(359 name, version=version, schema_url=schema_url, attributes=attributes360 )361 362 363class ProxyLoggerProvider(LoggerProvider):364 def get_logger(365 self,366 name: str,367 version: Optional[str] = None,368 schema_url: Optional[str] = None,369 attributes: Optional[_ExtendedAttributes] = None,370 ) -> Logger:371 if _LOGGER_PROVIDER:372 return _LOGGER_PROVIDER.get_logger(373 name,374 version=version,375 schema_url=schema_url,376 attributes=attributes,377 )378 return ProxyLogger(379 name,380 version=version,381 schema_url=schema_url,382 attributes=attributes,383 )384 385 386_LOGGER_PROVIDER_SET_ONCE = Once()387_LOGGER_PROVIDER: Optional[LoggerProvider] = None388_PROXY_LOGGER_PROVIDER = ProxyLoggerProvider()389 390 391def get_logger_provider() -> LoggerProvider:392 """Gets the current global :class:`~.LoggerProvider` object."""393 global _LOGGER_PROVIDER # pylint: disable=global-variable-not-assigned394 if _LOGGER_PROVIDER is None:395 if _OTEL_PYTHON_LOGGER_PROVIDER not in environ:396 return _PROXY_LOGGER_PROVIDER397 398 logger_provider: LoggerProvider = _load_provider( # type: ignore399 _OTEL_PYTHON_LOGGER_PROVIDER, "logger_provider"400 )401 _set_logger_provider(logger_provider, log=False)402 403 # _LOGGER_PROVIDER will have been set by one thread404 return cast("LoggerProvider", _LOGGER_PROVIDER)405 406 407def _set_logger_provider(logger_provider: LoggerProvider, log: bool) -> None:408 def set_lp() -> None:409 global _LOGGER_PROVIDER # pylint: disable=global-statement410 _LOGGER_PROVIDER = logger_provider411 412 did_set = _LOGGER_PROVIDER_SET_ONCE.do_once(set_lp)413 414 if log and not did_set:415 _logger.warning("Overriding of current LoggerProvider is not allowed")416 417 418def set_logger_provider(logger_provider: LoggerProvider) -> None:419 """Sets the current global :class:`~.LoggerProvider` object.420 421 This can only be done once, a warning will be logged if any further attempt422 is made.423 """424 _set_logger_provider(logger_provider, log=True)425 426 427def get_logger(428 instrumenting_module_name: str,429 instrumenting_library_version: str = "",430 logger_provider: Optional[LoggerProvider] = None,431 schema_url: Optional[str] = None,432 attributes: Optional[_ExtendedAttributes] = None,433) -> Logger:434 """Returns a `Logger` for use within a python process.435 436 This function is a convenience wrapper for437 opentelemetry.sdk._logs.LoggerProvider.get_logger.438 439 If logger_provider param is omitted the current configured one is used.440 """441 if logger_provider is None:442 logger_provider = get_logger_provider()443 return logger_provider.get_logger(444 instrumenting_module_name,445 instrumenting_library_version,446 schema_url,447 attributes,448 )449 