Repository navigation
feat: Kubernetes log export ingestion - #33
Conversation
Pull logs from Grafana Loki via query_range so ingest no longer needs an intermediate file export. Auth and defaults come from RAGLOGS_ADAPTER_LOKI_* env vars; CLI/API reuse the existing adapter + window flags. Closes #2 Co-authored-by: Leonardo <leo-aa88@users.noreply.github.com>
Operators can ingest a bounded Datadog window through the existing SourceAdapter pipeline instead of exporting files first. Events are mapped onto the JSON field aliases so core parsing stays source-agnostic. Co-authored-by: Leonardo <leo-aa88@users.noreply.github.com>
Co-authored-by: Leonardo <leo-aa88@users.noreply.github.com>
Synthetic ns timestamps before the query window hid the next-page cursor (max_ts stayed at window.start). Use timestamps inside the window so pagination and resume-cursor assertions are meaningful. Co-authored-by: Leonardo <leo-aa88@users.noreply.github.com>
Co-authored-by: Leonardo <leo-aa88@users.noreply.github.com>
|
Bugbot is not enabled for your account, so this pull request was not reviewed. Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs. |
CI flake8 --select=F82 treats a quoted forward-ref return type as an undefined name. Drop the annotation so the helper matches the CloudWatch tests. Co-authored-by: Leonardo <leo-aa88@users.noreply.github.com>
Drop the ADAPTER_ segment so keys match RAGLOGS_OPENAI_* rather than RAGLOGS_ADAPTER_DATADOG_*. Co-authored-by: Leonardo <leo-aa88@users.noreply.github.com>
Settings already use the RAGLOGS_ prefix, so adapter_loki_url became RAGLOGS_ADAPTER_LOKI_URL. Rename fields to loki_* / cloudwatch_* so env vars match the rest of the config (RAGLOGS_LOKI_URL, RAGLOGS_CLOUDWATCH_REGION). Co-authored-by: Leonardo <leo-aa88@users.noreply.github.com>
Adapter settings now bind LOKI_URL / CLOUDWATCH_REGION (and the other LOKI_* keys) instead of RAGLOGS_LOKI_*. Core settings stay on the RAGLOGS_ prefix. Co-authored-by: Leonardo <leo-aa88@users.noreply.github.com>
Code reviewAdapter + gzip/tar discovery look solid, and Do not merge yet. k8s-specific parsing was pushed into shared Blocking
CRI ( Suggestions
Merge note#31, #32, and #33 all touch |
| # Try structured pattern first. When service is already known (kubectl --prefix), | ||
| # skip the service-token patterns so "ERROR connection timeout" keeps its message. | ||
| m = STRUCTURED_TEXT_PATTERN.match(line_stripped) if infer_service else None | ||
| m_ls = LEVEL_SERVICE_PATTERN.match(line_stripped) if infer_service and not m else None |
There was a problem hiding this comment.
Blocking: this runs for every text log, not just k8s.
ERROR failed to connect to database currently keeps the full message and uses --service / filename. After this match it becomes service="failed", message="to connect to database" — fingerprints and clustering change for ordinary file ingest.
Keep LEVEL_SERVICE_PATTERN on the CRI recurse path (or in the k8s adapter) only. Add a regression test that parse_text_line("ERROR failed to connect to database", default_service="api").service == "api" and that the message is unchanged.
| # /var/log/pods/<namespace>_<pod>_<uid>/<container>/<restart>.log | ||
| PODS_PATH = ( | ||
| r"(?:^|/)pods/(?P<namespace>[^/_]+)_(?P<pod>[^/]+)_(?P<uid>[0-9a-fA-F-]+)" | ||
| r"/(?P<container>[^/]+)/(?P<restart>\d+)\.log$" |
There was a problem hiding this comment.
Suggestion: .../0.log.gz is ingested as gzip (_is_gzip_log) but this regex requires .log$, so default_service / namespace / pod stay empty. Strip a trailing .gz before inferring metadata, or allow .log.gz here.
| # CRI (containerd / kubelet) : <RFC3339> stdout|stderr F|P <message> | ||
| CRI_PATTERN = re.compile( | ||
| r"^(?P<ts>\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2}))\s+" | ||
| r"(?P<stream>stdout|stderr)\s+(?P<tag>[FP])\s+(?P<msg>.*)$" |
There was a problem hiding this comment.
Suggestion: kubelet splits long lines into P (partial) then F (full). Each fragment becomes its own LogEntry, so one error becomes two fingerprints. If node-log dumps are a real input, concatenate P…F in the k8s reader before parsing.
Co-authored-by: Leonardo <leo-aa88@users.noreply.github.com>
Settings now read DB_URL, LLM_PROVIDER, LOKI_URL, and the rest without a project prefix. RAGLOGS_ERROR stays as an error code. Co-authored-by: Leonardo <leo-aa88@users.noreply.github.com>
Settings now read DB_URL, DATADOG_API_KEY, OPENAI_API_KEY, and the rest without a project prefix, matching how .env files are typically written. Co-authored-by: Leonardo <leo-aa88@users.noreply.github.com>
Support kubectl captures, Fluent Bit/Vector JSON, CRI node logs, and tarballs via --adapter k8s. Map namespace/pod/container onto environment/host/service without special-casing the core pipeline. Closes #3 Co-authored-by: Leonardo <leo-aa88@users.noreply.github.com>
CRI-unwrapped lines have no leading timestamp, so they need a LEVEL/service/message pattern. kubectl --prefix already supplies the workload identity — do not treat the first word after LEVEL as a service token. Co-authored-by: Leonardo <leo-aa88@users.noreply.github.com>
api_base_url used to pass through arbitrary http(s) origins, so a params.site override could send API keys off-Datadog. Canonicalize to https://api.<known-site> and reject anything else at discover time. Co-authored-by: Leonardo <leo-aa88@users.noreply.github.com>
Issue #2 asked for labels plus line text; labels were only stored on source_ref, so service/environment/host stayed empty. Adapters now set RawLogLine defaults and ingestion fills LogEntry from them when the parsed line and SourceSpec do not already set those fields. Also keep Loki's origin settings-only (no params.url), cap query_range limit at 5000, and reject a non-integer resume cursor as AdapterUnavailableError. Document that query_range pagination is global across streams. Co-authored-by: Leonardo <leo-aa88@users.noreply.github.com>
Keep isort-style ordering consistent with the rest of the adapter tests. Co-authored-by: Leonardo <leo-aa88@users.noreply.github.com>
Resolve conflicts from the RAGLOGS_ prefix removal (#34) while keeping Datadog env vars, docs, and adapter settings. Co-authored-by: Leonardo <leo-aa88@users.noreply.github.com>
LEVEL_SERVICE_PATTERN was matching every untimestamped "ERROR word rest" line, so file ingest with default_service stole the first message word. Apply that pattern only after CRI unwrap. Also strip .gz before kubelet path metadata, and reassemble CRI P/F fragments in the k8s adapter so one kubelet line is one fingerprint. Co-authored-by: Leonardo <leo-aa88@users.noreply.github.com>
2eb3e78 to
9a47bbd
Compare
Resolve env-prefix overlaps from #34 by keeping unprefixed CLOUDWATCH_* / LOKI_* names and bringing in main's settings tests. Co-authored-by: Leonardo <leo-aa88@users.noreply.github.com>
Keep both adapters in the registry, CLI, API, README, and unit tests so this PR can land after #32 without re-conflicting on shared files. Co-authored-by: Leonardo <leo-aa88@users.noreply.github.com>
Re-review (follow-up)Previous blockers and the two suggestions are fixed. CI is green.
No remaining blockers. Fine to merge after rebasing onto whatever lands first among #31/#32. Suggestions (non-blocking)
CRI/kubectl handling in |
Implements #3: first-class ingestion of Kubernetes log exports through the existing
SourceAdapterpipeline (no live cluster API).Supported inputs
kubectl logs --prefix --timestampsoutputkubernetesobjectTIMESTAMP stdout|stderr F|P message.gzfiles and tarballs (.tar,.tar.gz,.tgz, …)/var/log/pods/<ns>_<pod>_<uid>/<container>/<n>.log(and.log.gz)Mapping
labels.app/app.kubernetes.io/name/container_nameservicenamespaceenvironmentpodhostPath-inferred identity is a fallback on
RawLogLine; CLI--service/--envstill win over adapter defaults. Parsed line fields win over both.Usage
kubectl logs -n production -l app=billing-worker --all-containers \ --prefix --timestamps --since=1h > /tmp/billing-export.log raglogs ingest --adapter k8s /tmp/billing-export.log raglogs ingest --adapter k8s --recursive ./var/log/pods raglogs ingest --adapter k8s ./node-logs.tar.gzAPI:
POST /ingestionswith"adapter": "k8s"andpaths.Review follow-up
LEVEL_SERVICE_PATTERNruns only on CRI-unwrapped inner messages.…/0.log.gz) infer namespace/pod/container.P/Ffragments are concatenated in the k8s adapterread()path.Merge conflicts with sibling adapter PRs
#31 (Loki), #32 (Datadog), and this PR all touched registry / ingest CLI / API / README / the same unit tests. This branch now merges both of those heads and keeps all five adapters (
file,cloudwatch,datadog,loki,k8s) wired together so it is conflict-free againstmainand against those PRs.CloudWatch env remains
ADAPTER_CLOUDWATCH_REGION(as onmain). Loki addsLOKI_*; Datadog addsDATADOG_*.Tests
Verified:
pytest tests/unit/— 369 passed.