-
Notifications
You must be signed in to change notification settings - Fork 703
Add initial overall structure and classes for logs sdk #1894
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,174 @@ | ||
# Copyright The OpenTelemetry Authors | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
import abc | ||
import atexit | ||
from typing import Any, Optional | ||
|
||
from opentelemetry.sdk.logs.severity import SeverityNumber | ||
from opentelemetry.sdk.resources import Resource | ||
from opentelemetry.sdk.util.instrumentation import InstrumentationInfo | ||
from opentelemetry.trace.span import TraceFlags | ||
from opentelemetry.util.types import Attributes | ||
|
||
|
||
class LogRecord: | ||
"""A LogRecord instance represents an event being logged. | ||
|
||
LogRecord instances are created and emitted via `LogEmitter` | ||
every time something is logged. They contain all the information | ||
pertinent to the event being logged. | ||
""" | ||
|
||
def __init__( | ||
self, | ||
timestamp: Optional[int] = None, | ||
trace_id: Optional[int] = None, | ||
span_id: Optional[int] = None, | ||
trace_flags: Optional[TraceFlags] = None, | ||
severity_text: Optional[str] = None, | ||
severity_number: Optional[SeverityNumber] = None, | ||
name: Optional[str] = None, | ||
body: Optional[Any] = None, | ||
resource: Optional[Resource] = None, | ||
attributes: Optional[Attributes] = None, | ||
): | ||
self.timestamp = timestamp | ||
self.trace_id = trace_id | ||
self.span_id = span_id | ||
self.trace_flags = trace_flags | ||
self.severity_text = severity_text | ||
self.severity_number = severity_number | ||
self.name = name | ||
self.body = body | ||
self.resource = resource | ||
self.attributes = attributes | ||
|
||
def __eq__(self, other: object) -> bool: | ||
if not isinstance(other, LogRecord): | ||
return NotImplemented | ||
return self.__dict__ == other.__dict__ | ||
|
||
|
||
class LogData: | ||
"""Readable LogRecord data plus associated InstrumentationLibrary.""" | ||
|
||
def __init__( | ||
self, | ||
log_record: LogRecord, | ||
instrumentation_info: InstrumentationInfo, | ||
): | ||
self.log_record = log_record | ||
self.instrumentation_info = instrumentation_info | ||
|
||
|
||
class LogProcessor(abc.ABC): | ||
"""Interface to hook the log record emitting action. | ||
|
||
Log processors can be registered directly using | ||
:func:`LogEmitterProvider.add_log_processor` and they are invoked | ||
in the same order as they were registered. | ||
codeboten marked this conversation as resolved.
Show resolved
Hide resolved
|
||
""" | ||
|
||
@abc.abstractmethod | ||
def emit(self, log_data: LogData): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be nice if the SpanProcessor would also have a method called There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, what would There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would expect it to mean the same thing as in the context of logs and metrics, that we're emitting the data from the processor. Currently this is called "on_end" in the span processor. |
||
"""Emits the `LogData`""" | ||
|
||
@abc.abstractmethod | ||
def shutdown(self): | ||
"""Called when a :class:`opentelemetry.sdk.logs.LogEmitter` is shutdown""" | ||
|
||
@abc.abstractmethod | ||
def force_flush(self, timeout_millis: int = 30000): | ||
"""Export all the received logs to the configured Exporter that have not yet | ||
been exported. | ||
|
||
Args: | ||
timeout_millis: The maximum amount of time to wait for logs to be | ||
exported. | ||
|
||
Returns: | ||
False if the timeout is exceeded, True otherwise. | ||
""" | ||
|
||
|
||
class LogEmitter: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this a subclass of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, I added a diagram below. I hope it helps. |
||
# TODO: Add multi_log_processor | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please add documentation as to what a log emitter is |
||
def __init__( | ||
self, | ||
resource: Resource, | ||
instrumentation_info: InstrumentationInfo, | ||
): | ||
self._resource = resource | ||
self._instrumentation_info = instrumentation_info | ||
|
||
def emit(self, record: LogRecord): | ||
ocelotl marked this conversation as resolved.
Show resolved
Hide resolved
|
||
# TODO: multi_log_processor.emit | ||
pass | ||
|
||
def flush(self): | ||
# TODO: multi_log_processor.force_flush | ||
pass | ||
|
||
|
||
class LogEmitterProvider: | ||
# TODO: Add multi_log_processor | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ditto to documentation here. |
||
def __init__( | ||
self, | ||
resource: Resource = Resource.create(), | ||
shutdown_on_exit: bool = True, | ||
): | ||
self._resource = resource | ||
self._at_exit_handler = None | ||
if shutdown_on_exit: | ||
self._at_exit_handler = atexit.register(self.shutdown) | ||
|
||
def get_log_emitter( | ||
self, | ||
instrumenting_module_name: str, | ||
instrumenting_module_verison: str = "", | ||
) -> LogEmitter: | ||
return LogEmitter( | ||
self._resource, | ||
InstrumentationInfo( | ||
instrumenting_module_name, instrumenting_module_verison | ||
), | ||
) | ||
|
||
def add_log_processor(self, log_processor: LogProcessor): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Isn't this weird? I mean, this is a method of the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not sure I understand your question. How else would we add a log processor? |
||
"""Registers a new :class:`LogProcessor` for this `LogEmitterProvider` instance. | ||
|
||
The log processors are invoked in the same order they are registered. | ||
""" | ||
# TODO: multi_log_processor.add_log_processor. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this TODO necessary here? |
||
|
||
def shutdown(self): | ||
"""Shuts down the log processors.""" | ||
# TODO: multi_log_processor.shutdown | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this TODO necessary here? |
||
if self._at_exit_handler is not None: | ||
atexit.unregister(self._at_exit_handler) | ||
self._at_exit_handler = None | ||
|
||
def force_flush(self, timeout_millis: int = 30000) -> bool: | ||
"""Force flush the log processors. | ||
|
||
Args: | ||
timeout_millis: The maximum amount of time to wait for logs to be | ||
exported. | ||
|
||
Returns: | ||
True if all the log processors flushes the logs within timeout, | ||
False otherwise. | ||
""" | ||
# TODO: multi_log_processor.force_flush | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this TODO necessary here? |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
# Copyright The OpenTelemetry Authors | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
import abc | ||
import enum | ||
from typing import Sequence | ||
|
||
from opentelemetry.sdk.logs import LogData | ||
|
||
|
||
class LogExportResult(enum.Enum): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this part of the spec? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, I think this will be eventually added to spec. Even I can send PR to get this ExportResult included in spec if you want. |
||
SUCCESS = 0 | ||
FAILURE = 1 | ||
|
||
|
||
class LogExporter(abc.ABC): | ||
"""Interface for exporting logs. | ||
|
||
Interface to be implemented by services that want to export logs received | ||
in their own format. | ||
|
||
To export data this MUST be registered to the :class`opentelemetry.sdk.logs.LogEmitter` using a | ||
log processor. | ||
""" | ||
|
||
@abc.abstractmethod | ||
def export(self, batch: Sequence[LogData]): | ||
"""Exports a batch of logs. | ||
|
||
Args: | ||
batch: The list of `LogData` objects to be exported | ||
|
||
Returns: | ||
The result of the export | ||
""" | ||
|
||
@abc.abstractmethod | ||
def shutdown(self): | ||
"""Shuts down the exporter. | ||
|
||
Called when the SDK is shut down. | ||
""" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
# Copyright The OpenTelemetry Authors | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
import enum | ||
|
||
|
||
class SeverityNumber(enum.Enum): | ||
"""Numerical value of severity. | ||
|
||
Smaller numerical values correspond to less severe events | ||
(such as debug events), larger numerical values correspond | ||
to more severe events (such as errors and critical events). | ||
|
||
See the `Log Data Model`_ spec for more info and how to map the | ||
severity from source format to OTLP Model. | ||
|
||
.. _Log Data Model: | ||
https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/logs/data-model.md#field-severitynumber | ||
""" | ||
|
||
UNSPECIFIED = 0 | ||
TRACE = 1 | ||
TRACE2 = 2 | ||
TRACE3 = 3 | ||
TRACE4 = 4 | ||
DEBUG = 5 | ||
DEBUG2 = 6 | ||
DEBUG3 = 7 | ||
DEBUG4 = 8 | ||
INFO = 9 | ||
INFO2 = 10 | ||
INFO3 = 11 | ||
INFO4 = 12 | ||
WARN = 13 | ||
WARN2 = 14 | ||
WARN3 = 15 | ||
WARN4 = 16 | ||
ERROR = 17 | ||
ERROR2 = 18 | ||
ERROR3 = 19 | ||
ERROR4 = 20 | ||
FATAL = 21 | ||
FATAL2 = 22 | ||
FATAL3 = 23 | ||
FATAL4 = 24 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This might need to be read-only in the future. Could save for separate PR.