h1v3 0.7.3 → 0.7.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -26,7 +26,8 @@ export async function inject({ database, authentication, metaURL, handleError =
26
26
  if (!metaURL) throw new Error("Missing metaURL");
27
27
  if (!authentication) throw new Error("Missing authentication");
28
28
 
29
- const meta = await fetch(metaURL).then(resp => resp.json())
29
+ // const meta = await fetch(metaURL).then(resp => resp.json())
30
+ const { default: meta } = await import(metaURL, { with: { type: "json" } });
30
31
 
31
32
  const log = logger(database, authentication, meta.o11y.path);
32
33
  const teamMembershipStoreFactory = ({ tid }) => eventStore(database, authentication, populateParameters({ tid }, meta.team.membership.path));
@@ -23,7 +23,7 @@ function rand62(n) {
23
23
 
24
24
  }
25
25
 
26
- export const timeId = () => `${Date.now()}-${rand62(21)}`;
26
+ export const timeId = suffix => `${Date.now()}-${suffix || rand62(21)}`;
27
27
 
28
28
  export const generateSpanId = () => generateLowerHex(8);
29
29
 
@@ -14,25 +14,35 @@ function munge(attributes) {
14
14
  export function logger(database, authentication, path, defaultAttributes) {
15
15
 
16
16
  const { db, ref, set } = database;
17
+ const { auth } = authentication;
17
18
 
18
- async function recordLog(trace_id, level, sharedAttributes, { body, parent_id, attributes }) {
19
-
20
- const span_id = generateSpanId();
21
- console.log(defaultAttributes, sharedAttributes, attributes);
22
- const data = {
23
- meta: { uid: authentication.auth.currentUser.uid },
24
- body,
25
- trace_id,
26
- span_id,
27
- severity_text: level,
28
- time_client_ms: Date.now(),
29
- attributes: munge({ ...defaultAttributes, ...sharedAttributes, ...attributes })
30
- };
31
- if (parent_id) data.parent_id = parent_id;
32
- const refPath = path.replace("$id", timeId());
33
- console.log("Logging", data, "to", refPath);
34
- return await set(ref(db, refPath), data);
19
+ async function recordLog(traceId, level, sharedAttributes, { message, parentSpanId, spanId, attributes }) {
35
20
 
21
+ let data;
22
+ try {
23
+
24
+ const uid = auth?.currentUser?.uid;
25
+ if (!uid) throw new Error("No current user id. Is the user logged in?");
26
+
27
+ spanId = spanId || generateSpanId();
28
+ data = {
29
+ meta: { uid },
30
+ message,
31
+ traceId,
32
+ spanId,
33
+ severity_text: level,
34
+ time_client_ms: Date.now(),
35
+ attributes: munge({ ...defaultAttributes, ...sharedAttributes, ...attributes })
36
+ };
37
+ if (parentSpanId) data.parentSpanId = parentSpanId;
38
+ const refPath = path.replace("$id", `${traceId}-${spanId}`);
39
+ return await set(ref(db, refPath), data);
40
+
41
+ } catch(err) {
42
+
43
+ console.error("Error dispatching o11y message", data, err);
44
+
45
+ }
36
46
 
37
47
  }
38
48
 
@@ -41,17 +51,13 @@ export function logger(database, authentication, path, defaultAttributes) {
41
51
  context(sharedAttributes) {
42
52
 
43
53
  const id = generateTraceId();
44
- return {
45
- id,
46
- trace: recordLog.bind(this, id, "TRACE", sharedAttributes),
47
- debug: recordLog.bind(this, id, "DEBUG", sharedAttributes),
48
- info: recordLog.bind(this, id, "INFO", sharedAttributes),
49
- warn: recordLog.bind(this, id, "WARN", sharedAttributes),
50
- error: recordLog.bind(this, id, "ERROR", sharedAttributes),
51
- fatal: recordLog.bind(this, id, "FATAL", sharedAttributes)
52
- };
54
+ const ret = { id };
55
+ for(const x of ["debug", "info", "notice", "warning", "error", "critical", "alert", "emergency"])
56
+ ret[x] = recordLog.bind(this, id, x.toUpperCase(), sharedAttributes);
57
+ return ret;
53
58
 
54
59
  }
60
+
55
61
  };
56
62
 
57
63
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "h1v3",
3
- "version": "0.7.3",
3
+ "version": "0.7.4",
4
4
  "description": "",
5
5
  "license": "MIT",
6
6
  "author": "",
@@ -11,12 +11,15 @@ function decideSeverity(newData) {
11
11
  return googleLogLevels.includes(text) ? text : "INFO";
12
12
 
13
13
  }
14
+ const DISPATCHED = "DISPATCHED";
15
+ const BLOCKED = "BLOCKED";
16
+
14
17
  /*
15
18
 
16
19
  message, severity, // required
17
20
  traceId, spanId, timestamp, // advised
18
21
  logData, // optional log line
19
- spanStart, spanEnd, spanAttributes // optional trace attributes
22
+ spanStart, spanEnd, spanAttributes, parentSpanId // optional trace attributes
20
23
 
21
24
  */
22
25
  function handleO11yEventWritten(observe, e) {
@@ -26,24 +29,66 @@ function handleO11yEventWritten(observe, e) {
26
29
  if (!newData.time_server_ms) {
27
30
 
28
31
  const now = new Date();
29
- const time_server_ms = now.valueOf();
30
32
  const timestamp = now.toISOString();
31
- const message = newData?.message || "Missing log message";
33
+ const time_server_ms = now.valueOf();
34
+ const { traceId, spanId, parentSpanId, ...rest } = newData;
35
+
36
+ const { id } = e.params;
37
+ const [id_traceId, id_spanId] = id.split("-");
38
+ let action;
39
+ if (traceId !== id_traceId || spanId !== id_spanId) {
40
+
41
+ action = blockEvent({ message: "Mismatched event id", timestamp, details: { id, traceId, spanId }});
42
+
43
+ } else if (!newData?.meta?.uid) {
44
+
45
+ action = blockEvent({ message: "Missing / invalid uid", timestamp, details: { uid: newData?.meta?.uid } });
46
+
47
+ } else {
48
+
49
+ action = dispatch({ rest, time_server_ms, traceId, spanId, parentSpanId, timestamp });
50
+
51
+ }
52
+
53
+ after.ref.update({
54
+ time_server_ms,
55
+ timestamp,
56
+ action
57
+ });
58
+
59
+ }
32
60
 
33
- const { traceId, spanId, ...rest } = newData;
34
- const logData = { ...rest, time_server_ms };
35
61
 
62
+ function dispatch({ rest, time_server_ms, traceId, spanId, parentSpanId, timestamp }) {
63
+
64
+ const message = newData?.message || "Missing log message";
65
+ const uid = newData.meta?.uid;
66
+ const logData = { ...rest, time_server_ms };
36
67
  observe({
37
68
  message,
38
69
  severity: decideSeverity(newData),
39
70
 
40
- traceId: newData.trace_id,
41
- spanId: newData.span_id,
71
+ traceId,
72
+ spanId,
73
+ parentSpanId,
42
74
  timestamp,
43
75
 
44
- logData
76
+ logData,
77
+ spanAttributes: { "user.id": uid }
78
+ });
79
+ return DISPATCHED;
80
+
81
+ }
82
+
83
+ function blockEvent({ message, timestamp, details }) {
84
+
85
+ observe({
86
+ message: `O11y event blocked: ${message}`,
87
+ severity: "CRITICAL",
88
+ timestamp,
89
+ logData: { ...details, newData }
45
90
  });
46
- after.ref.update({ time_server_ms });
91
+ return BLOCKED;
47
92
 
48
93
  }
49
94
 
@@ -73,29 +118,30 @@ export function configureO11yStore({ paths, onValueWritten, observe, region, ins
73
118
  read: false,
74
119
  write: all(
75
120
  // required
76
- assertNewDataHasString("body", 500, 1),
121
+ assertNewDataHasString("message", 500, 1),
77
122
  assertNewDataHasOneOf("severity_text", ...googleLogLevels),
78
123
  // trace context
79
124
  all(
80
- assertNewDataHasLowercaseHexCharacters("trace_id", 32),
81
- assertNewDataFieldDoesNotMatch("trace_id", "0*")
125
+ assertNewDataHasLowercaseHexCharacters("traceId", 32),
126
+ assertNewDataFieldDoesNotMatch("traceId", "0*")
82
127
  ),
83
128
  all(
84
- assertNewDataHasLowercaseHexCharacters("span_id", 16),
85
- assertNewDataFieldDoesNotMatch("span_id", "0*")
129
+ assertNewDataHasLowercaseHexCharacters("spanId", 16),
130
+ assertNewDataFieldDoesNotMatch("spanId", "0*")
86
131
  ),
87
- // forbid server time faking
132
+ // forbid server time and action faking
88
133
  assertNewDataDoesNotHave("time_server_ms"),
134
+ assertNewDataDoesNotHave("action"),
89
135
  // optional bits
90
136
  any(
91
137
  assertNewDataDoesNotHave("time_client_ms"),
92
138
  assertNewDataHasNumber("time_client_ms")
93
139
  ),
94
140
  any(
95
- assertNewDataDoesNotHave("parent_id"),
141
+ assertNewDataDoesNotHave("parentSpanId"),
96
142
  all(
97
- assertNewDataHasLowercaseHexCharacters("parent_id", 16),
98
- assertNewDataFieldDoesNotMatch("parent_id", "0*")
143
+ assertNewDataHasLowercaseHexCharacters("parentSpanId", 16),
144
+ assertNewDataFieldDoesNotMatch("parentSpanId", "0*")
99
145
  )
100
146
  )
101
147
  )
@@ -9,7 +9,7 @@ export function provider({ trace, projectId, serviceName, log }) {
9
9
  message, severity, // required
10
10
  traceId, spanId, timestamp, // advised
11
11
  logData, // optional log line
12
- spanStart, spanEnd, spanAttributes // optional trace attributes
12
+ spanStart, spanEnd, spanAttributes, parentSpanId // optional trace attributes
13
13
  }) => {
14
14
 
15
15
  if (!message) throw new Error("Missing message");
@@ -30,7 +30,7 @@ export function provider({ trace, projectId, serviceName, log }) {
30
30
  const startTime = asISOString(spanStart);
31
31
  const endTime = asISOString(spanEnd);
32
32
 
33
- await createSpan({ trace }, { serviceName, projectTraceSpanName, message, spanId, startTime, endTime, spanAttributes });
33
+ await createSpan({ trace }, { parentSpanId, serviceName, projectTraceSpanName, message, spanId, startTime, endTime, spanAttributes });
34
34
  await writeLogLine({ log, projectId }, { tracePath, spanId, severity, timestamp, message, logData });
35
35
 
36
36
  };
@@ -92,23 +92,25 @@ async function writeLogLine({ log, projectId }, { message, tracePath, spanId, se
92
92
 
93
93
  }
94
94
 
95
- async function createSpan({ trace }, { serviceName, projectTraceSpanName, spanId, startTime, endTime, spanAttributes, message }) {
95
+ async function createSpan({ trace }, { serviceName, projectTraceSpanName, spanId, parentSpanId, startTime, endTime, spanAttributes, message }) {
96
96
 
97
97
  const attributes = toTraceAttributes({
98
98
  "service.name": serviceName,
99
99
  message,
100
100
  ...spanAttributes
101
101
  });
102
+ const requestBody = {
103
+ attributes,
104
+ name: projectTraceSpanName,
105
+ spanId,
106
+ displayName: { value: serviceName },
107
+ startTime,
108
+ endTime
109
+ };
110
+ if (parentSpanId) requestBody.parentSpanId = parentSpanId;
102
111
  await trace.projects.traces.spans.createSpan({
103
112
  name: projectTraceSpanName,
104
- requestBody: {
105
- attributes,
106
- name: projectTraceSpanName,
107
- spanId,
108
- displayName: { value: serviceName },
109
- startTime,
110
- endTime
111
- }
113
+ requestBody
112
114
  });
113
115
 
114
116
  }
@@ -1,5 +1,5 @@
1
1
  import minimist from "minimist";
2
- import { loadConfiguration } from "../load-configuration.js";
2
+ import { loadConfiguration } from "../configuration/load-configuration.js"
3
3
 
4
4
  export async function main(commands) {
5
5