Skip to content

Commit ec6dd84

Browse files
[exporters] feat: add telementry support
1 parent 9766bbe commit ec6dd84

File tree

8 files changed

+115
-4
lines changed

8 files changed

+115
-4
lines changed

libs/exporters/docker-compose.yaml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
networks:
2+
default:
3+
name: garf-exporter
4+
driver: bridge
5+
services:
6+
exporter:
7+
image: ghcr.io/google/garf-exporter:latest
8+
command:
9+
- "--source=$GARF_EXPORTER_SOURCE"
10+
- "--config=/app/config.yaml"
11+
- "--logger=local"
12+
environment:
13+
- GARF_EXPORTER_SOURCE=${GARF_EXPORTER_SOURCE}
14+
- OTEL_SERVICE_NAME=garf-exporter
15+
- OTEL_EXPORTER_OTLP_ENDPOINT=http://garf-exporter-otel-collector:4317
16+
ports:
17+
- "8001:8000"
18+
volumes:
19+
- "${GARF_EXPORTER_CONFIG}:/app/config.yaml"
20+
jaeger:
21+
image: jaegertracing/all-in-one:latest
22+
ports:
23+
- "16686:16686"
24+
environment:
25+
- COLLECTOR_OTLP_ENABLED=true
26+
otel-collector:
27+
image: otel/opentelemetry-collector:latest
28+
container_name: garf-exporter-otel-collector
29+
command: ["--config=/etc/otel-collector-config.yml"]
30+
user: 0:0
31+
ports:
32+
- "4317:4317"
33+
- "4318:4318"
34+
volumes:
35+
- "${OTEL_COLLECTOR_CONFIG}:/etc/otel-collector-config.yml"

libs/exporters/garf_exporter/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,4 @@
1919
'GarfExporter',
2020
]
2121

22-
__version__ = '0.0.6'
22+
__version__ = '0.1.0'

libs/exporters/garf_exporter/entrypoints/cli.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,20 @@
2323
import asyncio
2424
import contextlib
2525
import datetime
26+
import logging
2627

2728
import fastapi
2829
import prometheus_client
2930
import requests
3031
import typer
3132
import uvicorn
3233
from garf_executors.entrypoints import utils as garf_utils
34+
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
3335
from typing_extensions import Annotated
3436

3537
import garf_exporter
3638
from garf_exporter import exporter_service
39+
from garf_exporter.entrypoints.tracer import initialize_tracer
3740

3841
typer_app = typer.Typer()
3942

@@ -49,8 +52,8 @@ def healthcheck(host: str, port: int) -> bool:
4952
the delay between exports. If this delta if greater than 1.5 check is failed.
5053
5154
Args:
52-
host: Hostname gaarf-exporter http server (i.e. localhost).
53-
port: Port gaarf-exporter http server is running (i.e. 8000).
55+
host: Hostname garf-exporter http server (i.e. localhost).
56+
port: Port garf-exporter http server is running (i.e. 8000).
5457
5558
5659
Returns:
@@ -79,10 +82,12 @@ def healthcheck(host: str, port: int) -> bool:
7982
return not is_lagged_export
8083

8184

85+
initialize_tracer()
8286
app = fastapi.FastAPI(debug=False)
8387
exporter = garf_exporter.GarfExporter()
8488
metrics_app = prometheus_client.make_asgi_app(registry=exporter.registry)
8589
app.mount('/metrics', metrics_app)
90+
FastAPIInstrumentor.instrument_app(app)
8691

8792

8893
async def start_metric_generation(
@@ -158,7 +163,7 @@ def main(
158163
logger: Annotated[
159164
str,
160165
typer.Option(help='Type of logging'),
161-
] = 'rich',
166+
] = 'local',
162167
namespace: Annotated[
163168
str,
164169
typer.Option(help='Namespace prefix for Prometheus'),
@@ -223,6 +228,7 @@ async def start_uvicorn():
223228
await server.serve()
224229

225230
asyncio.run(start_uvicorn())
231+
logging.shutdown()
226232

227233

228234
if __name__ == '__main__':
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Copyright 2025 Google LLC
2+
#
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 at
6+
#
7+
# https://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# 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 and
13+
# limitations under the License.
14+
15+
import os
16+
17+
from opentelemetry import trace
18+
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import (
19+
OTLPSpanExporter,
20+
)
21+
from opentelemetry.sdk.resources import Resource
22+
from opentelemetry.sdk.trace import TracerProvider
23+
from opentelemetry.sdk.trace.export import (
24+
BatchSpanProcessor,
25+
)
26+
27+
DEFAULT_SERVICE_NAME = 'garf-exporter'
28+
29+
30+
def initialize_tracer():
31+
resource = Resource.create(
32+
{'service.name': os.getenv('OTLP_SERVICE_NAME', DEFAULT_SERVICE_NAME)}
33+
)
34+
35+
tracer_provider = TracerProvider(resource=resource)
36+
37+
if otel_endpoint := os.getenv('OTEL_EXPORTER_OTLP_ENDPOINT'):
38+
otlp_processor = BatchSpanProcessor(
39+
OTLPSpanExporter(endpoint=otel_endpoint, insecure=True)
40+
)
41+
tracer_provider.add_span_processor(otlp_processor)
42+
trace.set_tracer_provider(tracer_provider)

libs/exporters/garf_exporter/exporter.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
import garf_core
2727
import prometheus_client
2828

29+
from garf_exporter.telemetry import tracer
30+
2931
logger = logging.getLogger(__name__)
3032

3133

@@ -92,6 +94,7 @@ def reset_registry(self) -> None:
9294
self.registry._collector_to_names.clear()
9395
self.registry._names_to_collectors.clear()
9496

97+
@tracer.start_as_current_span('export')
9598
def export(
9699
self,
97100
report: garf_core.GarfReport,

libs/exporters/garf_exporter/exporter_service.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929

3030
import garf_exporter
3131
from garf_exporter import collector
32+
from garf_exporter.telemetry import tracer
3233

3334
logger = logging.getLogger(__name__)
3435

@@ -112,6 +113,7 @@ def fetcher(self) -> garf_core.ApiReportFetcher:
112113
self._report_fetcher = fetcher(**self.source_parameters)
113114
return self._report_fetcher
114115

116+
@tracer.start_as_current_span('generate_metrics')
115117
def generate_metrics(
116118
self,
117119
request: GarfExporterRequest,
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Copyright 2025 Google LLC
2+
#
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 at
6+
#
7+
# https://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# 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 and
13+
# limitations under the License.
14+
15+
from opentelemetry import trace
16+
17+
tracer = trace.get_tracer(
18+
instrumenting_module_name='garf_exporter',
19+
)

libs/exporters/pyproject.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ dependencies = [
1212
"fastapi",
1313
"uvicorn",
1414
"typer",
15+
"opentelemetry-api",
16+
"opentelemetry-sdk",
17+
"opentelemetry-instrumentation-fastapi",
18+
"opentelemetry-exporter-otlp",
1519
]
1620
authors = [
1721
{name = "Andrei Markin", email = "andrey.markin.ppc@gmail.com"},

0 commit comments

Comments
 (0)