Skip to content

feat(filters): Logs Ingestion API for the time token tracker (2.7.0) - #191

Merged
owndev merged 8 commits into
mainfrom
feature/time-token-tracker-logs-ingestion
Oct 10, 2026
Merged

owndev merged 8 commits into
mainfrom
feature/time-token-tracker-logs-ingestion

Conversation

@owndev

@owndev owndev commented Oct 9, 2026 •

Copy link
Copy Markdown
Owner

Problem

time_token_tracker.py sends one record per response through the HTTP Data Collector API (workspace shared key). Microsoft ended support for it on September 14, 2026. Ingestion still works with TLS 1.2+, but the API gets no fixes and does not work with Azure Monitor Private Link (AMPLS). The replacement is the Logs Ingestion API: it is based on a data collection rule (DCR) and authenticates with a Microsoft Entra ID token.

Change (Time Token Tracker 2.6.2 → 2.7.0)

  • New mode valve LOG_ANALYTICS_INGESTION_API: auto (default) | logs_ingestion | data_collector | both.

    • With auto, records go to the Logs Ingestion API once its settings are complete. Otherwise they go through the Data Collector API exactly as in 2.6.2: same request, signature and headers, plus a one-time deprecation warning.
    • both writes each record to both APIs for a side-by-side migration. One failing side does not stop the other.
  • New valves, all with environment-variable defaults:

    • LOG_ANALYTICS_DCR_ENDPOINT, LOG_ANALYTICS_DCR_IMMUTABLE_ID, LOG_ANALYTICS_DCR_STREAM_NAME (derived from LOG_ANALYTICS_LOG_TYPE when empty);
    • LOG_ANALYTICS_AUTH_MODE, LOG_ANALYTICS_TENANT_ID, LOG_ANALYTICS_CLIENT_ID;
    • LOG_ANALYTICS_CLIENT_SECRET (EncryptedStr, password input);
    • LOG_ANALYTICS_AUTHORITY_HOST and LOG_ANALYTICS_INGESTION_SCOPE, for sovereign clouds.

    No valve is renamed or removed. LOG_ANALYTICS_LOG_TYPE can now also be set by environment variable; an empty variable counts as unset.

  • Tokens need no extra packages (aiohttp only):

    • client secret;
    • managed identity on App Service / Functions / Container Apps (IDENTITY_ENDPOINT) and on VMs (IMDS);
    • AKS workload identity (federated token file).

    Tokens are cached per identity and refreshed before they expire. Concurrent records share one token request, and a failed refresh keeps using the still-valid token. After a failed token request, or after repeated 401s with fresh tokens, new token requests pause for 30 s. The authority and the endpoint must use https.

  • Sending:

    • still in the background with a 10 s timeout (5 s to connect), so answers never wait;
    • any 2xx counts as success (the API answers 204);
    • 400, 401, 403, 404, 413 and 429 are logged once each with an actionable hint (role assignment, DCR ID, stream name, 1 MB limit, Retry-After);
    • no retries, as before.
  • Secrets: the client secret is decrypted only for the token request. The secret and the token never appear in logs, status events or error texts. Token errors are redacted before they are cut.

  • Docs (docs/setup-azure-log-analytics.md):

    • choosing the API;
    • a Logs Ingestion setup: permissions, the values to collect, checking the existing table with getschema;
    • Option A, migrate OpenWebuiMetrics_CL in place, with a DCR whose transformKql keeps the existing suffixed columns, so Power BI and saved queries keep working. Migration is irreversible; this is called out;
    • Option B, a new table side by side;
    • identity and role assignment (Monitoring Metrics Publisher), DCE and private link, sovereign clouds, troubleshooting.

    README and the docstring are updated as well.

  • E2E (filters suite, 57 → 101 checks):

    • mock_la.py now emulates the token endpoint, the managed-identity endpoints and the DCR ingestion endpoint (Bearer check, URI, body, 204, error injection).
    • The new ingest group covers mode selection, the fallback, both, token caching and refresh, the back-off, managed identity, every HTTP error, slow or hanging endpoints, and no secrets in the logs. That last check also covers DEBUG output, which the probe pipe now captures.

Verification

Merging main (#195, Azure 3.0.0) needed two test-harness changes: the managed-identity mock moved from port 9106 to 9107, because 9106 is now the Azure AI Search mock, and the ingest group gives the container its IDENTITY_ENDPOINT/IDENTITY_HEADER back when it ends, so a later azure suite in the same container keeps its managed identity. The filter itself is unchanged.

Run Result
ruff 0.11.10 check + format clean
Filters suite, v0.11.4-slim and v0.11.3-slim, normal and --strict-known 101 PASS, 0 FAIL, 0 KNOWN (each)
All suites, v0.11.4-slim, --strict-known 335 PASS, 0 FAIL, 1 KNOWN (infomaniak-name-prefix, no fix yet)
After merging main 4b80e24 (#195): all suites, v0.11.4-slim, normal and --strict-known 462 PASS, 0 FAIL, 1 KNOWN (infomaniak-name-prefix), 463 checks
After merging main: filters + azure, v0.11.3-slim, --strict-known 299 PASS, 0 FAIL, 0 KNOWN
Meta-test (E2E_MOCK_FAULT=500) 0 KNOWN
Mutation testing first round: 13 surviving mutants found, the tests extended. Each was then rebuilt and killed by its new check

Before the code was written, the API facts were checked against Microsoft Learn, the azure-rest-api-specs and the SDK sources, and a critic reviewed the spec.

Three reviews followed (API and docs, security and compatibility, mutation testing). All 18 findings were fixed, 7 of them major.

Not verified against a real Azure tenant

Closes #188

🤖 Generated with Claude Code

owndev and others added 2 commits October 9, 2026 14:29
…188)

Microsoft deprecated the HTTP Data Collector API the tracker sends
through (support ended 2026-09-14). The tracker now also sends through
the Azure Monitor Logs Ingestion API (data collection rule, Microsoft
Entra ID token from a client secret, a managed identity or AKS workload
identity) without new dependencies. The Data Collector request stays
byte-identical as the fallback for existing configurations and logs a
one-time deprecation warning.

- 10 new valves with environment defaults, LOG_ANALYTICS_LOG_TYPE gets
  one too; LOG_ANALYTICS_INGESTION_API selects auto, logs_ingestion,
  data_collector or both
- tokens cached per identity (one request per key, refreshed before
  expiry, a still-valid token outlives a failed refresh, 30 s back-off);
  the client secret is decrypted only for the token request and secrets
  and tokens are redacted from error texts
- one actionable log line per failed record, no retries
- docs: DCR templates for an in-place table migration and a side-by-side
  table, identity and role setup, sovereign clouds, troubleshooting
- e2e: token, DCR and managed identity routes in mock_la.py, a PROBE_ENV
  hook in the probe pipe, the filters.ingest group (34 checks) and two
  spec checks

+semver: minor

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Token errors of the App Service / Container Apps token service
({statusCode, message, correlationId}) were logged as "400 error - "
without a reason; they now show the message, the correlation ID and a
LOG_ANALYTICS_CLIENT_ID hint. Every field of a token error is redacted
before it is cut, so no fragment of a secret survives the truncation.
An empty LOG_ANALYTICS_LOG_TYPE variable counts as unset again, as in
2.6.2, instead of sending an empty Log-Type.

The e2e suite now kills the mutants the review found surviving:
second revocation, late 401, reuse before half the token lifetime,
a usable token inside the back-off, the back-off after 20 s, an
http:// authority, scope and tenant in the cache key, the DCR ID hint
once per value, auto with only the auth mode, both with one side
incomplete, redaction at the cut points, and the tracker's DEBUG
output (captured through the probe pipe's new PROBE_LOG hook). The
once-per-process warning checks no longer pass on a reused container
when the warning is never logged. The setup guide's troubleshooting
table covers the missing log lines.

+semver: none

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment thread tests/e2e/mocks/mock_la.py Fixed
CodeQL flags the secret_ok/assertion_ok values in the mock's log line
as clear-text logging of sensitive data. The HTTP status in the same
line already shows whether the credentials were accepted.

+semver: none

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@owndev
owndev marked this pull request as draft October 9, 2026 18:20
@owndev

owndev commented Oct 9, 2026

Copy link
Copy Markdown
Owner Author

Converted to draft: per maintainer decision this becomes Time Token Tracker 3.0.0 with the Logs Ingestion API only. The HTTP Data Collector path and its valves are removed; users who need it stay on 2.6.2. The rework is in progress; the description will be updated when it is ready.

Brings in Azure 2.8.1 (#190). The checks table in docs/testing.md now
lists both changes: azure 75 from #190 and filters 101 from this branch.

+semver: none

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@owndev
owndev marked this pull request as ready for review October 9, 2026 18:22
@owndev

owndev commented Oct 9, 2026

Copy link
Copy Markdown
Owner Author

Update: the 3.0.0 rework was stopped before any code change. This PR stays 2.7.0, backward compatible: Microsoft ended support for the HTTP Data Collector API but ingestion still works and no shutdown date is announced, and the both mode allows a safe side-by-side migration. Removing the Data Collector path is tracked in a follow-up issue (remove in 3.0.0 once Microsoft announces a shutdown date). The branch now also contains main (Azure 2.8.1, #190); the conflict in docs/testing.md is resolved.

owndev and others added 4 commits October 10, 2026 14:35
Brings in Azure AI Foundry 3.0.0 (#195): pipeline-side Azure AI Search,
mocks/mock_search.py on 127.0.0.1:9106 and the azure rag group.
Conflicts: README.md and docs/testing.md keep both sides (counts table:
azure 198 from main, filters 101 from this branch). The azure docs, pipeline
and suite conflicted only through the criss-cross merge base; this branch
never changed them, so main's version is taken.

+semver: none

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
mocks/mock_search.py (serve_all.py, Azure AI Foundry 3.0.0) listens on
127.0.0.1:9106 from the container start, so mock_la.py could not open its
App Service / IMDS listeners there and the filters suite could not start
its Log Analytics mock. mock_la.py now serves managed identity on
127.0.0.1:9107 and 127.0.0.2:9107; the filters suite, serve_all.py,
harness/config.py and docs/testing.md say so.

+semver: none

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ngest group

Since Azure AI Foundry 3.0.0, run.sh starts the container with
IDENTITY_ENDPOINT / IDENTITY_HEADER (App Service managed identity from
mocks/mock_search.py, azure rag group). The filters ingest group removed
every managed identity variable from the server process at its end, so an
azure suite after it (`run.sh filters azure`, or --reuse) lost its managed
identity. The group now restores the container's own values, which the
driver reads from its environment; the probe pipe's comment no longer
claims that the container sets none of them.

+semver: none

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Measured on the merged branch (v0.11.4-slim, normal and strict known
mode): 463 checks, 462 PASS / 1 KNOWN, 1000 s for all suites (filters
259 s with the ingest group); v0.11.3-slim gives the same azure and
filters counts. README.md said "about 300 checks in 7-10 minutes" again
since 87eb6fb, which undid 6d6d3ee; it now says about 500 checks in
15-20 minutes.

+semver: none

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@owndev
owndev merged commit eaaba91 into main Oct 10, 2026
8 checks passed
owndev added a commit that referenced this pull request Oct 10, 2026
…/gemini-native-tool-calling

Brings Time Token Tracker 2.7.0 with the Logs Ingestion API (#191),
merged with main 4b80e24, and its filters suite (mock_la.py with the
managed identity endpoints on 127.0.0.1/127.0.0.2:9107).

config.py keeps search 9106 with #191's comment on 9105/9107 next to
tools 9111 and MCP 9112. docs/testing.md keeps the native tool calling
section, takes #191's filters row, PROBE_ENV / PROBE_LOG, mock_la setup
and output file rows, and combines the counts (filters 101, total 531);
the layout lists :9106, :9111, :9112 and mock_la's :443, :9105, :9107.

+semver: none

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
owndev added a commit that referenced this pull request Oct 10, 2026
…ogs Ingestion API

Measured on the merged tree (main 4b80e24 + #191 + #193): 531 checks,
530 PASS / 1 KNOWN on v0.11.4-slim (normal and strict known mode) and
on v0.11.3-slim, about 19 minutes per full run. The sample output, the
timing paragraph, the counts header and the meta-test row (57 PASS /
370 FAIL / 0 KNOWN with main's files) now say so.

+semver: none

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@owndev
owndev deleted the feature/time-token-tracker-logs-ingestion branch October 10, 2026 17:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Time Token Tracker — add the Azure Monitor Logs Ingestion API (DCR, Entra ID) and keep the deprecated HTTP Data Collector API as a fallback

2 participants