mirror of
https://github.com/hanzoai/o11y.git
synced 2026-10-15 12:58:33 +00:00
The traces and logs statement/condition builders emitted the OTLP-fork columns
(timestamp, duration_nano, attributes_string/number/bool, resources_string,
resource_string_service$name, ts_bucket_start, parent_span_id, trace_flags,
scope_name) against event.span / event.log, which carry the 15-column envelope.
Every span lookup and log search failed Code 47 UNKNOWN_IDENTIFIER — executed and
proven before this change, not inferred.
TWO COLUMNS ADDED to event.span and event.log because no rewrite can satisfy them:
resource_fingerprint String — the builders stamp
'resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter)'
on the MAIN table, and __resource_filter reads event.span_resource /
event.log_resource keyed (org, seen_at_ts_bucket_start, fingerprint). The
fingerprint is a write-time hash of the whole resource label set — derivable
from NO envelope column. event.log's pre-existing UInt64 'resource' is
type-incompatible and empty.
ts_bucket_start UInt64 MATERIALIZED intDiv(toUnixTimestamp(time),1800)*1800 —
the builders put bucket-range predicates directly on the main table.
MATERIALIZED-from-time keeps writers 15-column-envelope-only and shares ONE
bucketing scheme with the four support tables that already speak
seen_at_ts_bucket_start.
EVERYTHING ELSE REWRITTEN onto the envelope — one naming scheme wins:
timestamp->time, parent_span_id->parent, duration_nano->duration,
kind_string->kind (the live kind IS the string enum, so numeric kind/status_code
predicates translate by enum NAME), status_code_string->status,
has_error->status='error', attributes_string[k]->attributes[k],
attributes_number[k]->toFloat64OrNull(attributes[k]),
attributes_bool[k]->attributes[k]='true', resources_string['service.name'] and
resource_string_service$name->service, http_host->host, the calculated columns
to attribute expressions, and the 28 deprecated camelCase aliases through
oldToNew to the SAME envelope targets — never to table columns.
spantypes TraceDB/TraceSummaryTable move o11y_traces/distributed_trace_summary ->
event/trace; impltracedetail builds its FROM from those constants. Swapping them
ALONE panics TestGetTraceSummary on a nil pointer — the store and its test move
together, which is why they are in this commit and not a smaller one.
Pins moved deliberately, each with the reason in place. live_proof_test.go in
three packages executes the builders' emitted SQL against the real store, so the
next dialect drift fails loudly instead of at runtime.
go build ./... and go test ./... green across every package.
Co-authored-by: Hanzo Dev <[email protected]>
80 lines
2.9 KiB
Go
80 lines
2.9 KiB
Go
package impltracedetail
|
|
|
|
// THROWAWAY live-plane proof. Gated on O11Y_LIVE_PROOF=1 + O11Y_PROOF_DSN.
|
|
// Runs the ACTUAL trace-detail store methods (span lookup) against the live
|
|
// event plane end to end. Deleted after the proof.
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/hanzoai/o11y/pkg/instrumentation/instrumentationtest"
|
|
"github.com/hanzoai/o11y/pkg/telemetrystore"
|
|
"github.com/hanzoai/o11y/pkg/telemetrystore/datastoretelemetrystore"
|
|
"github.com/hanzoai/o11y/pkg/types/telemetrytypes"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestLiveProofSpanLookup(t *testing.T) {
|
|
if os.Getenv("O11Y_LIVE_PROOF") != "1" {
|
|
t.Skip("live proof disabled")
|
|
}
|
|
dsn := os.Getenv("O11Y_PROOF_DSN")
|
|
require.NotEmpty(t, dsn, "O11Y_PROOF_DSN required")
|
|
ts, err := datastoretelemetrystore.New(
|
|
context.Background(),
|
|
instrumentationtest.New().ToProviderSettings(),
|
|
telemetrystore.Config{
|
|
Provider: "datastore",
|
|
Connection: telemetrystore.ConnectionConfig{MaxOpenConns: 2, MaxIdleConns: 1, DialTimeout: 5 * time.Second},
|
|
Datastore: telemetrystore.DatastoreConfig{DSN: dsn},
|
|
},
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
store := NewTraceStore(ts)
|
|
ctx := context.Background()
|
|
const traceID = "prooftrace01"
|
|
|
|
summary, err := store.GetTraceSummary(ctx, traceID)
|
|
require.NoError(t, err)
|
|
require.Equal(t, traceID, summary.TraceID)
|
|
require.Greater(t, summary.NumSpans, uint64(0))
|
|
t.Logf("summary: start=%s end=%s spans=%d", summary.Start, summary.End, summary.NumSpans)
|
|
|
|
spans, err := store.GetTraceSpans(ctx, traceID, summary)
|
|
require.NoError(t, err)
|
|
require.NotEmpty(t, spans)
|
|
t.Logf("GetTraceSpans: %d spans, first: id=%s svc=%s kind=%d kindStr=%s err=%v attrs=%d",
|
|
len(spans), spans[0].SpanID, spans[0].ServiceName, spans[0].Kind, spans[0].SpanKind, spans[0].HasError, len(spans[0].AttributesString))
|
|
|
|
minimal, err := store.GetMinimalSpans(ctx, traceID, summary.Start, summary.End)
|
|
require.NoError(t, err)
|
|
require.NotEmpty(t, minimal)
|
|
t.Logf("GetMinimalSpans: %d", len(minimal))
|
|
|
|
flame, err := store.GetFlamegraphSpans(ctx, traceID, summary.Start, summary.End, nil)
|
|
require.NoError(t, err)
|
|
require.NotEmpty(t, flame)
|
|
t.Logf("GetFlamegraphSpans: %d, first resources=%v", len(flame), flame[0].ResourcesString)
|
|
|
|
byIDs, err := store.GetTraceSpansByIDs(ctx, traceID, summary.Start, summary.End, []string{spans[0].SpanID})
|
|
require.NoError(t, err)
|
|
require.NotEmpty(t, byIDs)
|
|
t.Logf("GetTraceSpansByIDs: %d", len(byIDs))
|
|
|
|
counts, err := store.GetSpanCountByField(ctx, traceID, summary, telemetrytypes.TelemetryFieldKey{
|
|
Name: "service.name", FieldContext: telemetrytypes.FieldContextResource,
|
|
})
|
|
require.NoError(t, err)
|
|
t.Logf("GetSpanCountByField(service.name): %v", counts)
|
|
|
|
durations, err := store.GetSpanDurationByField(ctx, traceID, summary, telemetrytypes.TelemetryFieldKey{
|
|
Name: "service.name", FieldContext: telemetrytypes.FieldContextResource,
|
|
})
|
|
require.NoError(t, err)
|
|
t.Logf("GetSpanDurationByField(service.name): %v", durations)
|
|
}
|