-
Notifications
You must be signed in to change notification settings - Fork 964
/
telemetry.js
122 lines (109 loc) · 3.79 KB
/
telemetry.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
import path from 'node:path'
import { fileURLToPath } from 'node:url'
import { diag, DiagConsoleLogger, DiagLogLevel } from '@opentelemetry/api'
import opentelemetry, { SpanStatusCode } from '@opentelemetry/api'
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'
import { Resource } from '@opentelemetry/resources'
import {
NodeTracerProvider,
BatchSpanProcessor,
} from '@opentelemetry/sdk-trace-node'
import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'
import ci from 'ci-info'
import envinfo from 'envinfo'
import fs from 'fs-extra'
import system from 'systeminformation'
// JSON modules aren't stable yet, but if they were we could use them instead.
// See https://nodejs.org/dist/latest-v18.x/docs/api/esm.html#json-modules.
const { name: packageName, version: packageVersion } = fs.readJSONSync(
path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../package.json')
)
/**
* @type NodeTracerProvider
*/
let traceProvider
/**
* @type BatchSpanProcessor
*/
let traceProcessor
/**
* @type OTLPTraceExporter
*/
let traceExporter
export async function startTelemetry() {
diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.ERROR)
// Resources
const info = JSON.parse(
await envinfo.run(
{
System: ['OS', 'Shell'],
Binaries: ['Node', 'Yarn', 'npm'],
npmPackages: '@redwoodjs/*',
IDEs: ['VSCode'],
},
{ json: true }
)
)
// get shell name instead of path
const shell = info.System?.Shell // Windows doesn't always provide shell info, I guess
if (shell?.path?.match('/')) {
info.System.Shell.name = info.System.Shell.path.split('/').pop()
} else if (shell?.path.match('\\')) {
info.System.Shell.name = info.System.Shell.path.split('\\').pop()
}
const cpu = await system.cpu()
const mem = await system.mem()
const resource = Resource.default().merge(
new Resource({
[SemanticResourceAttributes.SERVICE_NAME]: packageName,
[SemanticResourceAttributes.SERVICE_VERSION]: packageVersion,
[SemanticResourceAttributes.OS_TYPE]: info.System?.OS?.split(' ')[0],
[SemanticResourceAttributes.OS_VERSION]: info.System?.OS?.split(' ')[1],
'shell.name': info.System?.Shell?.name,
'node.version': info.Binaries?.Node?.version,
'yarn.version': info.Binaries?.Yarn?.version,
'npm.version': info.Binaries?.npm?.version,
'vscode.version': info.IDEs?.VSCode?.version,
'cpu.count': cpu.physicalCores,
'memory.gb': Math.round(mem.total / 1073741824),
'env.node_env': process.env.NODE_ENV || null,
'ci.redwood': !!process.env.REDWOOD_CI,
'ci.isci': ci.isCI,
fingerprint: undefined, // We don't provide a fingerprint here because it needs a fully setup project
})
)
// Tracing
traceProvider = new NodeTracerProvider({
resource: resource,
})
traceExporter = new OTLPTraceExporter({
// TODO: Point this to somewhere permanent
url:
process.env.REDWOOD_REDIRECT_TELEMETRY ||
'https://master-axolotl.telemetry-analytics-jgmw.c66.me/v1/traces',
})
traceProcessor = new BatchSpanProcessor(traceExporter)
traceProvider.addSpanProcessor(traceProcessor)
traceProvider.register()
process.on('SIGTERM', async () => {
await shutdownTelemetry()
})
}
export async function shutdownTelemetry() {
try {
opentelemetry.trace.getActiveSpan()?.end()
await traceProvider?.shutdown()
await traceProcessor?.shutdown()
await traceExporter?.shutdown()
} catch (error) {
console.error('Telemetry error')
console.error(error)
}
}
export function recordErrorViaTelemetry(error) {
opentelemetry.trace.getActiveSpan()?.setStatus({
code: SpanStatusCode.ERROR,
message: error.toString().split('\n')[0],
})
opentelemetry.trace.getActiveSpan()?.recordException(error)
}