Skip to content
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

Adds environment variables for log exporter #3037

Merged
merged 13 commits into from
Jan 11, 2023
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Unreleased
- Adds environment variables for log exporter
([#3037](https://github.com/open-telemetry/opentelemetry-python/pull/3037))

## Version 1.15.0/0.36b0 (2022-12-09)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ dependencies = [
"grpcio >= 1.0.0, < 2.0.0",
"opentelemetry-api ~= 1.12",
"opentelemetry-proto == 1.16.0.dev",
"opentelemetry-sdk ~= 1.12",
"opentelemetry-sdk ~= 1.16.0.dev",
]

[project.optional-dependencies]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,15 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from os import environ
from typing import Optional, Sequence
from grpc import ChannelCredentials, Compression
from opentelemetry.exporter.otlp.proto.grpc.exporter import (
OTLPExporterMixin,
get_resource_data,
_get_credentials,
_translate_value,
environ_to_compression,
)
from opentelemetry.proto.collector.logs.v1.logs_service_pb2 import (
ExportLogsServiceRequest,
Expand All @@ -34,6 +37,15 @@
from opentelemetry.sdk._logs import LogData
from opentelemetry.sdk._logs.export import LogExporter, LogExportResult

from opentelemetry.sdk.environment_variables import (
OTEL_EXPORTER_OTLP_LOGS_CERTIFICATE,
OTEL_EXPORTER_OTLP_LOGS_COMPRESSION,
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT,
OTEL_EXPORTER_OTLP_LOGS_HEADERS,
OTEL_EXPORTER_OTLP_LOGS_INSECURE,
OTEL_EXPORTER_OTLP_LOGS_TIMEOUT,
)


class OTLPLogExporter(
LogExporter,
Expand All @@ -52,13 +64,40 @@ def __init__(
timeout: Optional[int] = None,
compression: Optional[Compression] = None,
):
if insecure is None:
insecure = environ.get(OTEL_EXPORTER_OTLP_LOGS_INSECURE)
if insecure is not None:
insecure = insecure.lower() == "true"

if (
not insecure
and environ.get(OTEL_EXPORTER_OTLP_LOGS_CERTIFICATE) is not None
):
credentials = _get_credentials(
credentials, OTEL_EXPORTER_OTLP_LOGS_CERTIFICATE
)

environ_timeout = environ.get(OTEL_EXPORTER_OTLP_LOGS_TIMEOUT)
environ_timeout = (
int(environ_timeout) if environ_timeout is not None else None
)

compression = (
environ_to_compression(OTEL_EXPORTER_OTLP_LOGS_COMPRESSION)
if compression is None
else compression
)
endpoint = endpoint or environ.get(OTEL_EXPORTER_OTLP_LOGS_ENDPOINT)

headers = headers or environ.get(OTEL_EXPORTER_OTLP_LOGS_HEADERS)

super().__init__(
**{
"endpoint": endpoint,
"insecure": insecure,
"credentials": credentials,
"headers": headers,
"timeout": timeout,
"timeout": timeout or environ_timeout,
"compression": compression,
}
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,13 @@

import time
from concurrent.futures import ThreadPoolExecutor
from os.path import dirname
from unittest import TestCase
from unittest.mock import patch

from google.protobuf.duration_pb2 import Duration
from google.rpc.error_details_pb2 import RetryInfo
from grpc import StatusCode, server
from grpc import ChannelCredentials, Compression, StatusCode, server

from opentelemetry._logs import SeverityNumber
from opentelemetry.exporter.otlp.proto.grpc._log_exporter import (
Expand Down Expand Up @@ -47,10 +48,19 @@
)
from opentelemetry.sdk._logs import LogData, LogRecord
from opentelemetry.sdk._logs.export import LogExportResult
from opentelemetry.sdk.environment_variables import (
OTEL_EXPORTER_OTLP_LOGS_CERTIFICATE,
OTEL_EXPORTER_OTLP_LOGS_COMPRESSION,
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT,
OTEL_EXPORTER_OTLP_LOGS_HEADERS,
OTEL_EXPORTER_OTLP_LOGS_TIMEOUT,
)
from opentelemetry.sdk.resources import Resource as SDKResource
from opentelemetry.sdk.util.instrumentation import InstrumentationScope
from opentelemetry.trace import TraceFlags

THIS_DIR = dirname(__file__)


class LogsServiceServicerUNAVAILABLEDelay(LogsServiceServicer):
# pylint: disable=invalid-name,unused-argument,no-self-use
Expand Down Expand Up @@ -100,7 +110,6 @@ def Export(self, request, context):

class TestOTLPLogExporter(TestCase):
def setUp(self):

self.exporter = OTLPLogExporter()

self.server = server(ThreadPoolExecutor(max_workers=10))
Expand Down Expand Up @@ -164,6 +173,32 @@ def test_exporting(self):
# pylint: disable=protected-access
self.assertEqual(self.exporter._exporting, "logs")

@patch.dict(
"os.environ",
{
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT: "logs:4317",
OTEL_EXPORTER_OTLP_LOGS_CERTIFICATE: THIS_DIR
+ "/../fixtures/test.cert",
OTEL_EXPORTER_OTLP_LOGS_HEADERS: " key1=value1,KEY2 = VALUE=2",
OTEL_EXPORTER_OTLP_LOGS_TIMEOUT: "10",
OTEL_EXPORTER_OTLP_LOGS_COMPRESSION: "gzip",
},
)
@patch(
"opentelemetry.exporter.otlp.proto.grpc.exporter.OTLPExporterMixin.__init__"
)
def test_env_variables(self, mock_exporter_mixin):
OTLPLogExporter()

self.assertTrue(len(mock_exporter_mixin.call_args_list) == 1)
_, kwargs = mock_exporter_mixin.call_args_list[0]
self.assertEqual(kwargs["endpoint"], "logs:4317")
self.assertEqual(kwargs["headers"], " key1=value1,KEY2 = VALUE=2")
self.assertEqual(kwargs["timeout"], 10)
self.assertEqual(kwargs["compression"], Compression.Gzip)
self.assertIsNotNone(kwargs["credentials"])
self.assertIsInstance(kwargs["credentials"], ChannelCredentials)

@patch(
"opentelemetry.exporter.otlp.proto.grpc.exporter.ssl_channel_credentials"
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ dependencies = [
"googleapis-common-protos ~= 1.52",
"opentelemetry-api ~= 1.12",
"opentelemetry-proto == 1.16.0.dev",
"opentelemetry-sdk ~= 1.12",
"opentelemetry-sdk ~= 1.16.0.dev",
"requests ~= 2.7",
]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@
OTEL_EXPORTER_OTLP_ENDPOINT,
OTEL_EXPORTER_OTLP_HEADERS,
OTEL_EXPORTER_OTLP_TIMEOUT,
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT,
OTEL_EXPORTER_OTLP_LOGS_CERTIFICATE,
OTEL_EXPORTER_OTLP_LOGS_HEADERS,
OTEL_EXPORTER_OTLP_LOGS_TIMEOUT,
OTEL_EXPORTER_OTLP_LOGS_COMPRESSION,
)
from opentelemetry.sdk._logs import LogData
from opentelemetry.sdk._logs.export import (
Expand Down Expand Up @@ -79,16 +84,26 @@ def __init__(
compression: Optional[Compression] = None,
session: Optional[requests.Session] = None,
):
self._endpoint = endpoint or _append_logs_path(
environ.get(OTEL_EXPORTER_OTLP_ENDPOINT, DEFAULT_ENDPOINT)
self._endpoint = endpoint or environ.get(
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT,
_append_logs_path(
environ.get(OTEL_EXPORTER_OTLP_ENDPOINT, DEFAULT_ENDPOINT)
),
)
self._certificate_file = certificate_file or environ.get(
OTEL_EXPORTER_OTLP_CERTIFICATE, True
OTEL_EXPORTER_OTLP_LOGS_CERTIFICATE,
environ.get(OTEL_EXPORTER_OTLP_CERTIFICATE, True),
)
headers_string = environ.get(
OTEL_EXPORTER_OTLP_LOGS_HEADERS,
environ.get(OTEL_EXPORTER_OTLP_HEADERS, ""),
)
headers_string = environ.get(OTEL_EXPORTER_OTLP_HEADERS, "")
self._headers = headers or parse_env_headers(headers_string)
self._timeout = timeout or int(
environ.get(OTEL_EXPORTER_OTLP_TIMEOUT, DEFAULT_TIMEOUT)
environ.get(
OTEL_EXPORTER_OTLP_LOGS_TIMEOUT,
environ.get(OTEL_EXPORTER_OTLP_TIMEOUT, DEFAULT_TIMEOUT),
)
)
self._compression = compression or _compression_from_env()
self._session = session or requests.Session()
Expand Down Expand Up @@ -170,7 +185,12 @@ def shutdown(self):

def _compression_from_env() -> Compression:
compression = (
environ.get(OTEL_EXPORTER_OTLP_COMPRESSION, "none").lower().strip()
environ.get(
OTEL_EXPORTER_OTLP_LOGS_COMPRESSION,
environ.get(OTEL_EXPORTER_OTLP_COMPRESSION, "none"),
)
.lower()
.strip()
)
return Compression(compression)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@
OTEL_EXPORTER_OTLP_COMPRESSION,
OTEL_EXPORTER_OTLP_ENDPOINT,
OTEL_EXPORTER_OTLP_HEADERS,
OTEL_EXPORTER_OTLP_LOGS_CERTIFICATE,
OTEL_EXPORTER_OTLP_LOGS_COMPRESSION,
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT,
OTEL_EXPORTER_OTLP_LOGS_HEADERS,
OTEL_EXPORTER_OTLP_LOGS_TIMEOUT,
OTEL_EXPORTER_OTLP_TIMEOUT,
)
from opentelemetry.sdk.resources import Resource as SDKResource
Expand Down Expand Up @@ -92,6 +97,38 @@ def test_constructor_default(self):
"application/x-protobuf",
)

@patch.dict(
"os.environ",
{
OTEL_EXPORTER_OTLP_CERTIFICATE: ENV_CERTIFICATE,
OTEL_EXPORTER_OTLP_COMPRESSION: Compression.Gzip.value,
OTEL_EXPORTER_OTLP_ENDPOINT: ENV_ENDPOINT,
OTEL_EXPORTER_OTLP_HEADERS: ENV_HEADERS,
OTEL_EXPORTER_OTLP_TIMEOUT: ENV_TIMEOUT,
OTEL_EXPORTER_OTLP_LOGS_CERTIFICATE: "logs/certificate.env",
OTEL_EXPORTER_OTLP_LOGS_COMPRESSION: Compression.Deflate.value,
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT: "https://logs.endpoint.env",
OTEL_EXPORTER_OTLP_LOGS_HEADERS: "logsEnv1=val1,logsEnv2=val2,logsEnv3===val3==",
OTEL_EXPORTER_OTLP_LOGS_TIMEOUT: "40",
},
)
def test_exporter_metrics_env_take_priority(self):
exporter = OTLPLogExporter()

self.assertEqual(exporter._endpoint, "https://logs.endpoint.env")
self.assertEqual(exporter._certificate_file, "logs/certificate.env")
self.assertEqual(exporter._timeout, 40)
self.assertIs(exporter._compression, Compression.Deflate)
self.assertEqual(
exporter._headers,
{
"logsenv1": "val1",
"logsenv2": "val2",
"logsenv3": "==val3==",
},
)
self.assertIsInstance(exporter._session, requests.Session)

@patch.dict(
"os.environ",
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,15 @@
A scheme of https indicates a secure connection and takes precedence over this configuration setting.
"""

OTEL_EXPORTER_OTLP_LOGS_ENDPOINT = "OTEL_EXPORTER_OTLP_LOGS_ENDPOINT"
"""
.. envvar:: OTEL_EXPORTER_OTLP_LOGS_ENDPOINT

The :envvar:`OTEL_EXPORTER_OTLP_LOGS_ENDPOINT` target to which the log exporter is going to send logs.
The endpoint MUST be a valid URL host, and MAY contain a scheme (http or https), port and path.
A scheme of https indicates a secure connection and takes precedence over this configuration setting.
"""

OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE = "OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE"
"""
.. envvar:: OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE
Expand Down Expand Up @@ -378,6 +387,14 @@
associated with gRPC or HTTP requests.
"""

OTEL_EXPORTER_OTLP_LOGS_HEADERS = "OTEL_EXPORTER_OTLP_LOGS_HEADERS"
"""
.. envvar:: OTEL_EXPORTER_OTLP_LOGS_HEADERS

The :envvar:`OTEL_EXPORTER_OTLP_LOGS_HEADERS` contains the key-value pairs to be used as headers for logs
associated with gRPC or HTTP requests.
"""

OTEL_EXPORTER_OTLP_TRACES_COMPRESSION = "OTEL_EXPORTER_OTLP_TRACES_COMPRESSION"
"""
.. envvar:: OTEL_EXPORTER_OTLP_TRACES_COMPRESSION
Expand All @@ -396,6 +413,14 @@
exporter. If both are present, this takes higher precedence.
"""

OTEL_EXPORTER_OTLP_LOGS_COMPRESSION = "OTEL_EXPORTER_OTLP_LOGS_COMPRESSION"
"""
.. envvar:: OTEL_EXPORTER_OTLP_LOGS_COMPRESSION

Same as :envvar:`OTEL_EXPORTER_OTLP_COMPRESSION` but only for the log
exporter. If both are present, this takes higher precedence.
"""

OTEL_EXPORTER_OTLP_TRACES_TIMEOUT = "OTEL_EXPORTER_OTLP_TRACES_TIMEOUT"
"""
.. envvar:: OTEL_EXPORTER_OTLP_TRACES_TIMEOUT
Expand All @@ -421,6 +446,15 @@
Default: False
"""

OTEL_EXPORTER_OTLP_LOGS_INSECURE = "OTEL_EXPORTER_OTLP_LOGS_INSECURE"
"""
.. envvar:: OTEL_EXPORTER_OTLP_LOGS_INSECURE

The :envvar:`OTEL_EXPORTER_OTLP_LOGS_INSECURE` represents whether to enable client transport security
for gRPC requests for metrics. A scheme of https takes precedence over the this configuration setting.
Default: False
"""

OTEL_EXPORTER_OTLP_METRICS_ENDPOINT = "OTEL_EXPORTER_OTLP_METRICS_ENDPOINT"
"""
.. envvar:: OTEL_EXPORTER_OTLP_METRICS_ENDPOINT
Expand All @@ -440,6 +474,14 @@
TLS credentials of gRPC client for traces. Should only be used for a secure connection for tracing.
"""

OTEL_EXPORTER_OTLP_LOGS_CERTIFICATE = "OTEL_EXPORTER_OTLP_LOGS_CERTIFICATE"
"""
.. envvar:: OTEL_EXPORTER_OTLP_LOGS_CERTIFICATE

The :envvar:`OTEL_EXPORTER_OTLP_LOGS_CERTIFICATE` stores the path to the certificate file for
TLS credentials of gRPC client for traces. Should only be used for a secure connection for tracing.
"""

OTEL_EXPORTER_OTLP_METRICS_HEADERS = "OTEL_EXPORTER_OTLP_METRICS_HEADERS"
"""
.. envvar:: OTEL_EXPORTER_OTLP_METRICS_HEADERS
Expand All @@ -456,6 +498,14 @@
wait for each batch export for metrics.
"""

OTEL_EXPORTER_OTLP_LOGS_TIMEOUT = "OTEL_EXPORTER_OTLP_LOGS_TIMEOUT"
"""
.. envvar:: OTEL_EXPORTER_OTLP_LOGS_TIMEOUT

The :envvar:`OTEL_EXPORTER_OTLP_LOGS_TIMEOUT` is the maximum time the OTLP exporter will
wait for each batch export for logs.
"""

OTEL_EXPORTER_OTLP_METRICS_COMPRESSION = (
"OTEL_EXPORTER_OTLP_METRICS_COMPRESSION"
)
Expand Down