Ga naar inhoud

Testen met Logfire

Mogelijk moet u controleren of uw API 's de gegevens registreren die u verwacht, het werk volgen dat ze correct inpakken, enz. Dit is vaak moeilijk, inclusief het gebruik van de ingebouwde logging van Python en de SDK van OpenTelemetry.

Logfire maakt het eenvoudig om logboeken en overspanningen te testen met behulp van de hulpprogramma 's in de logfire.testing module. Dit is hoe Logfire zichzelf intern test.

capfire Lampen

Het heeft twee eigenschappen,exporter en metrics_reader .

exporter

Dit is een exemplaar van TestExporter en is een OpenTelemetry SDK-compatibele span-exporteur die geëxporteerde overspanningen in het geheugen houdt.

Met de exporter.exported_spans_as_dict() -methode kunt u een eenvoudige woordenboekweergave van de geëxporteerde span verkrijgen, die u gemakkelijk kunt laten gelden en waaruit u goede verschillen kunt afleiden. Deze benadering doet wat gegevensverwerking om de uitvoer leesbaarder en deterministischer te maken, bijvoorbeeld door regelnummers te vervangen door 123 en bestandspaden door bestandsnamen.

test.py
import pytest

import logfire
from logfire.testing import  CaptureLogfire


def test_observability(capfire: CaptureLogfire) -> None:
    with pytest.raises(Exception):
        with logfire.span('a span!'):
            logfire.info('a log!')
            raise Exception('an exception!')

    exporter = capfire.exporter

    # insert_assert(exporter.exported_spans_as_dict()) (1)
    assert exporter.exported_spans_as_dict() == [
        {
            'name': 'a log!',
            'context': {'trace_id': 1, 'span_id': 3, 'is_remote': False},
            'parent': {'trace_id': 1, 'span_id': 1, 'is_remote': False},
            'start_time': 2000000000,
            'end_time': 2000000000,
            'attributes': {
                'logfire.span_type': 'log',
                'logfire.level_num': 9,
                'logfire.msg_template': 'a log!',
                'logfire.msg': 'a log!',
                'code.filepath': 'test.py',
                'code.lineno': 123,
                'code.function': 'test_observability',
            },
        },
        {
            'name': 'a span!',
            'context': {'trace_id': 1, 'span_id': 1, 'is_remote': False},
            'parent': None,
            'start_time': 1000000000,
            'end_time': 4000000000,
            'attributes': {
                'code.filepath': 'test.py',
                'code.lineno': 123,
                'code.function': 'test_observability',
                'logfire.msg_template': 'a span!',
                'logfire.span_type': 'span',
                'logfire.msg': 'a span!',
            },
            'events': [
                {
                    'name': 'exception',
                    'timestamp': 3000000000,
                    'attributes': {
                        'exception.type': 'Exception',
                        'exception.message': 'an exception!',
                        'exception.stacktrace': 'Exception: an exception!',
                        'exception.escaped': 'True',
                    },
                }
            ],
        },
    ]
  1. insert_assert is een hulpprogramma functie geleverd door DevTools.

    Zie er meer over hieronder.

U kunt toegang krijgen tot de geëxporteerde overspanning met 按exporter.exported_spans.

import logfire
from logfire.testing import CaptureLogfire


def test_exported_spans(capfire: CaptureLogfire) -> None:
    with logfire.span('a span!'):
        logfire.info('a log!')

    exporter = capfire.exporter

    expected_span_names = ['a span! (pending)', 'a log!', 'a span!']
    span_names = [span.name for span in exporter.exported_spans]

    assert span_names == expected_span_names

U kunt exporter.clear() aanroepen om de in de test vastgelegde overspanning opnieuw in te stellen.

import logfire
from logfire.testing import CaptureLogfire


def test_reset_exported_spans(capfire: CaptureLogfire) -> None:
    exporter = capfire.exporter

    assert len(exporter.exported_spans) == 0

    logfire.info('First log!')
    assert len(exporter.exported_spans) == 1
    assert exporter.exported_spans[0].name == 'First log!'

    logfire.info('Second log!')
    assert len(exporter.exported_spans) == 2
    assert exporter.exported_spans[1].name == 'Second log!'

    exporter.clear()
    assert len(exporter.exported_spans) == 0

    logfire.info('Third log!')
    assert len(exporter.exported_spans) == 1
    assert exporter.exported_spans[0].name == 'Third log!'

metrics_reader

Dit is een exemplaar van InMemoryMetricReader dat de metriek in het geheugen leest.

import json
from typing import cast

from opentelemetry.sdk.metrics.export import MetricsData

from logfire.testing import CaptureLogfire


def test_system_metrics_collection(capfire: CaptureLogfire) -> None:
    exported_metrics = json.loads(cast(MetricsData, capfire.metrics_reader.get_metrics_data()).to_json())  # type: ignore

    metrics_collected = {
        metric['name']
        for resource_metric in exported_metrics['resource_metrics']
        for scope_metric in resource_metric['scope_metrics']
        for metric in scope_metric['metrics']
    }

    # collected metrics vary by platform, etc.
    # assert that we at least collected _some_ of the metrics we expect
    assert metrics_collected.issuperset(
        {
            'system.swap.usage',
            'system.disk.operations',
            'system.memory.usage',
            'system.cpu.utilization',
        }
    ), metrics_collected

Laten we eens kijken naar de hulpprogramma 's die we gebruiken.

IncrementalId生成器

Een van de meest complexe dingen bij het vergelijken van logboekuitvoer met verwachte resultaten is de bron van niet-determinisme. Voor OpenTelemetry-overspanningen zijn de twee grootste overspanningen span en trace-ID en tijdstempel.

IncrementalIdGenerator Verhoog de span en trace ID 's opeenvolgend, zodat de testuitvoer altijd hetzelfde is.

时间生成器

Deze klasse genereert een nanoseconde tijdstempel die elke keer dat een tijdstempel wordt gegenereerd met 1 seconde wordt verhoogd.

logfire.配置

Dit is dezelfde configuratiefunctie die u bij de productie gebruikt en het komt allemaal samen.

Houd er rekening mee dat we specifiek configureren:

  • send_to_logfire=False, omdat we de daadwerkelijke productiedienst niet willen aanraken

  • id_generator=IncrementalIdGenerator() Maak span ID deterministisch

  • ns_timestamp_generator=TimeGenerator() Tijdstempel deterministisch maken

  • processors=[SimpleSpanProcessor(exporter)] gebruikt onze TestExporter om overspanningen vast te leggen. We gebruiken SimpleSpanProcessor om overspanningen zonder vertraging te exporteren.

insert_assert

Dit is een hulpprogramma-functie die wordt geleverd door devtools die automatisch de uitvoer van het aanroepen in het testbestand invoegt wanneer het door pytest wordt uitgevoerd. Dat wil zeggen, als je die regel becommentarieert, zul je zien dat de 断言 capfire.exported_spans_as_dict() == [...]-lijn wordt vervangen door de huidige uitvoer van capfire.exported_spans_as_dict(), wat precies hetzelfde zou moeten zijn, aangezien onze test deterministisch is!


本文总阅读量次