Skip to content

Commit 52aa201

Browse files
[executors] feat: switch to typer for CLI
* Add missing __init__.py to entrypoints * Unify logging across library
1 parent 27bae2d commit 52aa201

File tree

7 files changed

+118
-50
lines changed

7 files changed

+118
-50
lines changed

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.2'
22+
__version__ = '0.0.3'

libs/exporters/garf_exporter/entrypoints/__init__.py

Whitespace-only changes.

libs/exporters/garf_exporter/entrypoints/cli.py

Lines changed: 79 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,24 @@
2020

2121
from __future__ import annotations
2222

23-
import argparse
2423
import asyncio
2524
import contextlib
2625
import datetime
26+
from typing import Literal
2727

2828
import fastapi
29-
import garf_exporter
3029
import prometheus_client
3130
import requests
31+
import typer
3232
import uvicorn
3333
from garf_executors.entrypoints import utils as garf_utils
34+
from typing_extensions import Annotated
35+
36+
import garf_exporter
3437
from garf_exporter import exporter_service
3538

39+
typer_app = typer.Typer()
40+
3641

3742
class GarfExporterError(Exception):
3843
"""Base class for GarfExporter errors."""
@@ -80,12 +85,6 @@ def healthcheck(host: str, port: int) -> bool:
8085
metrics_app = prometheus_client.make_asgi_app(registry=exporter.registry)
8186
app.mount('/metrics', metrics_app)
8287

83-
logger = garf_utils.init_logging(
84-
loglevel='INFO',
85-
logger_type='rich',
86-
name='garf-exporter',
87-
)
88-
8988

9089
async def start_metric_generation(
9190
request: exporter_service.GarfExporterRequest,
@@ -129,44 +128,82 @@ def health(request: fastapi.Request):
129128
raise fastapi.HTTPException(status_code=404, detail='Not updated properly')
130129

131130

132-
def main() -> None: # noqa: D103
133-
parser = argparse.ArgumentParser()
134-
parser.add_argument('-s', '--source', dest='source')
135-
parser.add_argument('-c', '--config', dest='config', default=None)
136-
parser.add_argument('--log', '--loglevel', dest='loglevel', default='info')
137-
parser.add_argument(
138-
'--expose-type',
139-
dest='expose_type',
140-
choices=['http', 'pushgateway'],
141-
default='http',
142-
)
143-
parser.add_argument('--host', dest='host', default='0.0.0.0')
144-
parser.add_argument('--port', dest='port', type=int, default=8000)
145-
parser.add_argument('--logger', dest='logger', default='local')
146-
parser.add_argument('--iterations', dest='iterations', default=None, type=int)
147-
parser.add_argument('--delay-minutes', dest='delay', type=int, default=15)
148-
parser.add_argument('--namespace', dest='namespace', default='garf')
149-
parser.add_argument('--max-parallel', dest='parallel', default=None)
150-
parser.add_argument(
151-
'--fetching-timeout-seconds', dest='fetching_timeout', default=120, type=int
131+
@typer_app.command(
132+
context_settings={'allow_extra_args': True, 'ignore_unknown_options': True}
133+
)
134+
def main(
135+
ctx: typer.Context,
136+
source: Annotated[
137+
str,
138+
typer.Option(
139+
'-s', '--source', help='API alias', prompt='Please add API alias'
140+
),
141+
],
142+
config: Annotated[
143+
str,
144+
typer.Option(
145+
'-c',
146+
'--config',
147+
help='Path to configuration file',
148+
prompt='Please add path to config file.',
149+
),
150+
],
151+
expose_type: Annotated[
152+
str,
153+
typer.Option(help='Type of metric expose'),
154+
] = 'http',
155+
loglevel: Annotated[
156+
str,
157+
typer.Option(help='Level of logging'),
158+
] = 'INFO',
159+
logger: Annotated[
160+
str,
161+
typer.Option(help='Type of logging'),
162+
] = 'rich',
163+
namespace: Annotated[
164+
str,
165+
typer.Option(help='Namespace prefix for Prometheus'),
166+
] = 'garf',
167+
host: Annotated[
168+
str,
169+
typer.Option(help='Host for exposing metrics'),
170+
] = '0.0.0.0',
171+
port: Annotated[
172+
int,
173+
typer.Option(help='Port for exposing metrics'),
174+
] = 8000,
175+
delay_minutes: Annotated[
176+
int,
177+
typer.Option(help='Delay in minutes between exports'),
178+
] = 15,
179+
fetching_timeout: Annotated[
180+
int,
181+
typer.Option(help='Timeout in second for restarting stale exports'),
182+
] = 120,
183+
iterations: Annotated[
184+
int,
185+
typer.Option(help='Stop export after N iterations'),
186+
] = 0,
187+
) -> None:
188+
garf_utils.init_logging(
189+
loglevel=loglevel,
190+
logger_type=logger,
191+
name='garf-exporter',
152192
)
153-
parser.add_argument('--collectors', dest='collectors', default='default')
154-
parser.add_argument('-v', '--version', dest='version', action='store_true')
155-
args, kwargs = parser.parse_known_args()
156-
cli_parameters = garf_utils.ParamsParser(['macro', 'source']).parse(kwargs)
193+
cli_parameters = garf_utils.ParamsParser(['macro', 'source']).parse(ctx.args)
157194
runtime_options = exporter_service.GarfExporterRuntimeOptions(
158-
expose_type=args.expose_type,
159-
host=args.host,
160-
port=args.port,
161-
namespace=args.namespace,
162-
fetching_timeout=args.fetching_timeout,
163-
iterations=args.iterations,
164-
delay_minutes=args.delay,
195+
expose_type=expose_type,
196+
host=host,
197+
port=port,
198+
namespace=namespace,
199+
fetching_timeout=fetching_timeout,
200+
iterations=iterations,
201+
delay_minutes=delay_minutes,
165202
)
166203
request = exporter_service.GarfExporterRequest(
167-
source=args.source,
204+
source=source,
168205
source_parameters=cli_parameters.get('source'),
169-
collectors_config=args.config,
206+
collectors_config=config,
170207
macros=cli_parameters.get('macro'),
171208
runtime_options=runtime_options,
172209
)
@@ -187,4 +224,4 @@ async def start_uvicorn():
187224

188225

189226
if __name__ == '__main__':
190-
main()
227+
typer_app()

libs/exporters/garf_exporter/exporter.py

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

29+
logger = logging.getLogger(__name__)
30+
2931

3032
class GarfExporter:
3133
"""Exposes reports from Ads API in Prometheus format.
@@ -181,7 +183,7 @@ def _define_metrics(
181183
if virtual_columns := query_specification.virtual_columns:
182184
for column, field in virtual_columns.items():
183185
metrics[column] = self._define_gauge(column, suffix, labels, namespace)
184-
logging.debug('metrics: %s', metrics)
186+
logger.debug('metrics: %s', metrics)
185187
return metrics
186188

187189
def _define_labels(
@@ -205,7 +207,7 @@ def _define_labels(
205207
for column, field in zip(non_virtual_columns, query_specification.fields):
206208
if 'metric' not in field and 'metric' not in column:
207209
labelnames.append(str(column))
208-
logging.debug('labelnames: %s', labelnames)
210+
logger.debug('labelnames: %s', labelnames)
209211
return labelnames
210212

211213
def _define_gauge(

libs/exporters/garf_exporter/exporter_service.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
import garf_exporter
3131
from garf_exporter import collector
3232

33+
logger = logging.getLogger(__name__)
34+
3335

3436
class GarfExporterRuntimeOptions(pydantic.BaseModel):
3537
"""Options to finetune exporting process.
@@ -121,14 +123,14 @@ def generate_metrics(
121123
request: Complete request to fetch and expose data.
122124
exporter: Initialized GarfExporter.
123125
"""
124-
logging.info('Beginning export')
126+
logger.info('Beginning export')
125127
start_export_time = time()
126128
exporter.export_started.set(start_export_time)
127129
collectors = request.collectors or collector.load_collector_data(
128130
request.collectors_config
129131
)
130132
for col in collectors:
131-
logging.info('Exporting from collector: %s', col.title)
133+
logger.info('Exporting from collector: %s', col.title)
132134
start = time()
133135
report = self.fetcher.fetch(col.query, **self.source_parameters)
134136
end = time()
@@ -138,7 +140,7 @@ def generate_metrics(
138140
suffix=col.suffix,
139141
collector=col.title,
140142
)
141-
logging.info('Export completed')
143+
logger.info('Export completed')
142144
end_export_time = time()
143145
exporter.export_completed.set(end_export_time)
144146
exporter.total_export_time_gauge.set(end_export_time - start_export_time)

libs/exporters/pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ dependencies = [
1111
"pydantic",
1212
"fastapi",
1313
"uvicorn",
14+
"typer",
1415
]
1516
authors = [
1617
{name = "Andrei Markin", email = "andrey.markin.ppc@gmail.com"},
@@ -39,4 +40,4 @@ dynamic = ["version"]
3940
version = {attr = "garf_exporter.__version__"}
4041

4142
[project.scripts]
42-
garf-exporter="garf_exporter.entrypoints.cli:main"
43+
garf-exporter="garf_exporter.entrypoints.cli:typer_app"

libs/exporters/uv.lock

Lines changed: 27 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)