diff --git a/migrations/20240120195122_init.up.sql b/migrations/20240120195122_init.up.sql index 9389092..265e90e 100644 --- a/migrations/20240120195122_init.up.sql +++ b/migrations/20240120195122_init.up.sql @@ -8,8 +8,8 @@ CREATE TABLE IF NOT EXISTS spans( "__duration" INTEGER, "name" TEXT, "kind" TEXT, - "start_time" INTEGER, - "end_time" INTEGER, + "start_time" INTEGER, -- start_time is a microsecond precision unix timestamp + "end_time" INTEGER, -- end_time is a microsecond precision unix timestamp "status_code" INTEGER, "status_description" TEXT, "attributes" TEXT, @@ -23,7 +23,7 @@ CREATE TABLE IF NOT EXISTS spans( CREATE TABLE IF NOT EXISTS events( "span_id" BLOB, - "timestamp" INTEGER, + "timestamp" INTEGER, -- timestamp is a microsecond precision unix timestamp "name" TEXT, "attributes" TEXT, "dropped_attributes_count" INTEGER, diff --git a/migrations/20240209144007_microsecond_timestamps.down.sql b/migrations/20240209144007_microsecond_timestamps.down.sql new file mode 100644 index 0000000..31da5aa --- /dev/null +++ b/migrations/20240209144007_microsecond_timestamps.down.sql @@ -0,0 +1,4 @@ +ALTER TABLE spans DROP COLUMN start_time; +ALTER TABLE spans DROP COLUMN end_time; +ALTER TABLE spans RENAME COLUMN start_time_bak TO start_time; +ALTER TABLE spans RENAME COLUMN end_time_back TO end_time; diff --git a/migrations/20240209144007_microsecond_timestamps.up.sql b/migrations/20240209144007_microsecond_timestamps.up.sql new file mode 100644 index 0000000..eff459c --- /dev/null +++ b/migrations/20240209144007_microsecond_timestamps.up.sql @@ -0,0 +1,7 @@ +-- Backup the start_time and end_time column values and convert existing +-- timestamps to microsecond precision +-- TODO(wperron): remove this before publishing +ALTER TABLE spans RENAME COLUMN start_time TO start_time_bak; +ALTER TABLE spans RENAME COLUMN end_time TO end_time; +ALTER TABLE spans ADD COLUMN (start_time INTEGER ) DEFAULT (start_time_bak * 1000); +ALTER TABLE spans ADD COLUMN (end_time INTEGER ) DEFAULT (end_time * 1000); diff --git a/sqlite_exporter.go b/sqlite_exporter.go index d8d1ebf..4e4d8cd 100644 --- a/sqlite_exporter.go +++ b/sqlite_exporter.go @@ -6,6 +6,7 @@ import ( "database/sql" "encoding/json" "fmt" + "time" _ "github.com/mattn/go-sqlite3" "go.opentelemetry.io/collector/component" @@ -198,8 +199,9 @@ func (e *sqliteExporter) ConsumeTraces(ctx context.Context, traces ptrace.Traces dur.Microseconds(), span.Name(), span.Kind().String(), - span.StartTimestamp().AsTime().Unix(), - span.EndTimestamp().AsTime().Unix(), + // Use microsecond precision for start and timestamps + unixMicro(span.StartTimestamp().AsTime()), + unixMicro(span.EndTimestamp().AsTime()), span.Status().Code(), span.Status().Message(), attrs, @@ -228,7 +230,7 @@ func (e *sqliteExporter) ConsumeTraces(ctx context.Context, traces ptrace.Traces _, err = stmt.ExecContext(ctx, spanidbs, - event.Timestamp().AsTime().Unix(), + unixMicro(event.Timestamp().AsTime()), event.Name(), attrs, event.DroppedAttributesCount(), @@ -278,3 +280,7 @@ func (e *sqliteExporter) ConsumeTraces(ctx context.Context, traces ptrace.Traces func pcommonMapAsJSON(m pcommon.Map) ([]byte, error) { return json.Marshal(m.AsRaw()) } + +func unixMicro(t time.Time) int64 { + return t.UnixNano() / 1000 +} diff --git a/sqlite_exporter_test.go b/sqlite_exporter_test.go index 8bfce0f..3a55e27 100644 --- a/sqlite_exporter_test.go +++ b/sqlite_exporter_test.go @@ -152,8 +152,8 @@ func Test_ExporterExportSpan(t *testing.T) { assert.Equal(t, 5000, duration) assert.Equal(t, "span1", name) assert.Equal(t, ptrace.SpanKindServer.String(), kind) - assert.Equal(t, now.Add(-5*time.Millisecond).Unix(), int64(startTime)) - assert.Equal(t, now.Unix(), int64(endTime)) + assert.Equal(t, unixMicro(now.Add(-5*time.Millisecond)), int64(startTime)) + assert.Equal(t, unixMicro(now), int64(endTime)) assert.Equal(t, int(ptrace.StatusCodeOk), statusCode) assert.Equal(t, ptrace.StatusCodeOk.String(), statusDescription) assert.Equal(t, "{\"http.method\":\"GET\",\"http.status_code\":200}", attributes) @@ -191,8 +191,8 @@ func Test_ExporterExportSpan(t *testing.T) { assert.Equal(t, 3000, duration) assert.Equal(t, "span2", name) assert.Equal(t, ptrace.SpanKindInternal.String(), kind) - assert.Equal(t, now.Add(-4*time.Millisecond).Unix(), int64(startTime)) - assert.Equal(t, now.Add(-1*time.Millisecond).Unix(), int64(endTime)) + assert.Equal(t, unixMicro(now.Add(-4*time.Millisecond)), int64(startTime)) + assert.Equal(t, unixMicro(now.Add(-1*time.Millisecond)), int64(endTime)) assert.Equal(t, int(ptrace.StatusCodeOk), statusCode) assert.Equal(t, ptrace.StatusCodeOk.String(), statusDescription) assert.Equal(t, "{\"custom.key\":\"custom-value\"}", attributes)