@monocle.sh/studio 0.1.0
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.
- package/LICENSE +15 -0
- package/dist/adonisrc.d.ts +6 -0
- package/dist/adonisrc.js +25 -0
- package/dist/config/app.d.ts +6 -0
- package/dist/config/app.js +5 -0
- package/dist/config/bodyparser.d.ts +4 -0
- package/dist/config/bodyparser.js +5 -0
- package/dist/config/encryption.d.ts +12 -0
- package/dist/config/encryption.js +11 -0
- package/dist/config/logger.d.ts +4 -0
- package/dist/config/logger.js +13 -0
- package/dist/config/static.d.ts +6 -0
- package/dist/config/static.js +11 -0
- package/dist/controllers/api_controller.d.ts +174 -0
- package/dist/controllers/api_controller.js +118 -0
- package/dist/controllers/mcp_controller.d.ts +20 -0
- package/dist/controllers/mcp_controller.js +29 -0
- package/dist/controllers/otlp_controller.d.ts +52 -0
- package/dist/controllers/otlp_controller.js +92 -0
- package/dist/controllers/sse_controller.d.ts +18 -0
- package/dist/controllers/sse_controller.js +36 -0
- package/dist/db/connection.d.ts +20 -0
- package/dist/db/connection.js +38 -0
- package/dist/db/schema.d.ts +10 -0
- package/dist/db/schema.js +45 -0
- package/dist/db/types.d.ts +40 -0
- package/dist/db/types.js +1 -0
- package/dist/mcp/define_tool.d.ts +10 -0
- package/dist/mcp/define_tool.js +9 -0
- package/dist/mcp/tools/clear_data.d.ts +9 -0
- package/dist/mcp/tools/clear_data.js +26 -0
- package/dist/mcp/tools/get_trace.d.ts +12 -0
- package/dist/mcp/tools/get_trace.js +25 -0
- package/dist/mcp/tools/index.d.ts +10 -0
- package/dist/mcp/tools/index.js +37 -0
- package/dist/mcp/tools/list_errors.d.ts +14 -0
- package/dist/mcp/tools/list_errors.js +25 -0
- package/dist/mcp/tools/list_logs.d.ts +16 -0
- package/dist/mcp/tools/list_logs.js +30 -0
- package/dist/mcp/tools/list_queries.d.ts +15 -0
- package/dist/mcp/tools/list_queries.js +27 -0
- package/dist/mcp/tools/list_traces.d.ts +14 -0
- package/dist/mcp/tools/list_traces.js +25 -0
- package/dist/mcp/types.d.ts +21 -0
- package/dist/mcp/types.js +1 -0
- package/dist/mcp.d.ts +2 -0
- package/dist/mcp.js +2 -0
- package/dist/providers/api_provider.d.ts +14 -0
- package/dist/providers/api_provider.js +13 -0
- package/dist/repositories/log_repository.d.ts +43 -0
- package/dist/repositories/log_repository.js +58 -0
- package/dist/repositories/span_repository.d.ts +136 -0
- package/dist/repositories/span_repository.js +254 -0
- package/dist/semconv.d.ts +38 -0
- package/dist/semconv.js +40 -0
- package/dist/server.d.ts +16 -0
- package/dist/server.js +68 -0
- package/dist/services/event_bus.d.ts +24 -0
- package/dist/services/event_bus.js +24 -0
- package/dist/services/otlp_parser.d.ts +21 -0
- package/dist/services/otlp_parser.js +121 -0
- package/dist/start/kernel.d.ts +1 -0
- package/dist/start/kernel.js +5 -0
- package/dist/start/routes.d.ts +1 -0
- package/dist/start/routes.js +35 -0
- package/dist/transformers/command_transformer.d.ts +18 -0
- package/dist/transformers/command_transformer.js +20 -0
- package/dist/transformers/exception_transformer.d.ts +17 -0
- package/dist/transformers/exception_transformer.js +19 -0
- package/dist/transformers/job_transformer.d.ts +18 -0
- package/dist/transformers/job_transformer.js +20 -0
- package/dist/transformers/log_transformer.d.ts +18 -0
- package/dist/transformers/log_transformer.js +19 -0
- package/dist/transformers/query_transformer.d.ts +20 -0
- package/dist/transformers/query_transformer.js +27 -0
- package/dist/transformers/span_transformer.d.ts +22 -0
- package/dist/transformers/span_transformer.js +23 -0
- package/dist/transformers/trace_transformer.d.ts +22 -0
- package/dist/transformers/trace_transformer.js +33 -0
- package/dist/types/logs.d.ts +19 -0
- package/dist/types/logs.js +1 -0
- package/dist/types/otlp.d.ts +105 -0
- package/dist/types/otlp.js +1 -0
- package/dist/types/spans.d.ts +29 -0
- package/dist/types/spans.js +1 -0
- package/dist/validators.d.ts +103 -0
- package/dist/validators.js +41 -0
- package/package.json +60 -0
- package/ui/dist/assets/geist-cyrillic-wght-normal-CHSlOQsW.woff2 +0 -0
- package/ui/dist/assets/geist-latin-ext-wght-normal-DMtmJ5ZE.woff2 +0 -0
- package/ui/dist/assets/geist-latin-wght-normal-Dm3htQBi.woff2 +0 -0
- package/ui/dist/assets/index-Bfk6GRvP.css +1 -0
- package/ui/dist/assets/index-XOaGlb1r.js +115 -0
- package/ui/dist/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2 +0 -0
- package/ui/dist/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2 +0 -0
- package/ui/dist/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
- package/ui/dist/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
- package/ui/dist/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2 +0 -0
- package/ui/dist/assets/silkscreen-latin-400-normal-CtPo2yA5.woff2 +0 -0
- package/ui/dist/assets/silkscreen-latin-400-normal-D0DfPJut.woff +0 -0
- package/ui/dist/favicon.svg +4 -0
- package/ui/dist/index.html +14 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
ISC License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024-present, Julien Ripouteau
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
6
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
7
|
+
copyright notice and this permission notice appear in all copies.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
10
|
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
11
|
+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
12
|
+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
13
|
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
14
|
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
15
|
+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
package/dist/adonisrc.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { indexEntities } from "@adonisjs/core";
|
|
2
|
+
import { defineConfig } from "@adonisjs/core/app";
|
|
3
|
+
import { generateRegistry } from "@tuyau/core/hooks";
|
|
4
|
+
//#region src/adonisrc.ts
|
|
5
|
+
var adonisrc_default = defineConfig({
|
|
6
|
+
directories: { public: "../ui/dist" },
|
|
7
|
+
providers: [
|
|
8
|
+
() => import("@adonisjs/core/providers/app_provider"),
|
|
9
|
+
() => import("@adonisjs/core/providers/vinejs_provider"),
|
|
10
|
+
() => import("@adonisjs/static/static_provider")
|
|
11
|
+
],
|
|
12
|
+
preloads: [
|
|
13
|
+
() => import("./providers/api_provider.js"),
|
|
14
|
+
() => import("./start/kernel.js"),
|
|
15
|
+
() => import("./start/routes.js")
|
|
16
|
+
],
|
|
17
|
+
hooks: { init: [indexEntities({ transformers: {
|
|
18
|
+
enabled: true,
|
|
19
|
+
source: "./src",
|
|
20
|
+
glob: ["transformers/*_transformer.ts"],
|
|
21
|
+
importAlias: "#app"
|
|
22
|
+
} }), generateRegistry()] }
|
|
23
|
+
});
|
|
24
|
+
//#endregion
|
|
25
|
+
export { adonisrc_default as default };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import * as _adonisjs_core_types_encryption0 from "@adonisjs/core/types/encryption";
|
|
2
|
+
import * as _adonisjs_core_types0 from "@adonisjs/core/types";
|
|
3
|
+
|
|
4
|
+
//#region src/config/encryption.d.ts
|
|
5
|
+
declare const _default: _adonisjs_core_types0.ConfigProvider<{
|
|
6
|
+
default?: "app" | undefined;
|
|
7
|
+
list: {
|
|
8
|
+
app: _adonisjs_core_types_encryption0.EncryptionConfig;
|
|
9
|
+
};
|
|
10
|
+
}>;
|
|
11
|
+
//#endregion
|
|
12
|
+
export { _default as default };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { defineConfig, drivers } from "@adonisjs/core/encryption";
|
|
2
|
+
//#region src/config/encryption.ts
|
|
3
|
+
var encryption_default = defineConfig({
|
|
4
|
+
default: "app",
|
|
5
|
+
list: { app: drivers.aes256gcm({
|
|
6
|
+
id: "app",
|
|
7
|
+
keys: [process.env.APP_KEY]
|
|
8
|
+
}) }
|
|
9
|
+
});
|
|
10
|
+
//#endregion
|
|
11
|
+
export { encryption_default as default };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { defineConfig, targets } from "@adonisjs/core/logger";
|
|
2
|
+
//#region src/config/logger.ts
|
|
3
|
+
const config = defineConfig({
|
|
4
|
+
default: "app",
|
|
5
|
+
loggers: { app: {
|
|
6
|
+
enabled: true,
|
|
7
|
+
name: "monocle-devtools",
|
|
8
|
+
level: "info",
|
|
9
|
+
transport: { targets: targets().push(targets.pretty()).toArray() }
|
|
10
|
+
} }
|
|
11
|
+
});
|
|
12
|
+
//#endregion
|
|
13
|
+
export { config as default };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { defineConfig } from "@adonisjs/static";
|
|
2
|
+
//#region src/config/static.ts
|
|
3
|
+
var static_default = defineConfig({
|
|
4
|
+
enabled: true,
|
|
5
|
+
immutable: true,
|
|
6
|
+
maxAge: "1y",
|
|
7
|
+
cacheControl: true,
|
|
8
|
+
etag: true
|
|
9
|
+
});
|
|
10
|
+
//#endregion
|
|
11
|
+
export { static_default as default };
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { HttpContext } from "@adonisjs/core/http";
|
|
2
|
+
//#region src/controllers/api_controller.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* HTTP API controller exposing telemetry data collected by the
|
|
5
|
+
* OTLP ingester. Serves paginated lists of traces, logs,
|
|
6
|
+
* exceptions, queries, jobs, and commands.
|
|
7
|
+
*/
|
|
8
|
+
declare class ApiController {
|
|
9
|
+
/**
|
|
10
|
+
* Returns all distinct service names that have emitted spans.
|
|
11
|
+
*/
|
|
12
|
+
listServices({
|
|
13
|
+
response
|
|
14
|
+
}: HttpContext): Promise<{
|
|
15
|
+
__response: {
|
|
16
|
+
data: string[];
|
|
17
|
+
};
|
|
18
|
+
__status: 200;
|
|
19
|
+
}>;
|
|
20
|
+
/**
|
|
21
|
+
* Lists HTTP request traces with optional service and trace ID filters.
|
|
22
|
+
*/
|
|
23
|
+
listTraces({
|
|
24
|
+
request,
|
|
25
|
+
serialize
|
|
26
|
+
}: HttpContext): Promise<{
|
|
27
|
+
data: {
|
|
28
|
+
traceId: string;
|
|
29
|
+
rootSpanName: string;
|
|
30
|
+
serviceName: string;
|
|
31
|
+
method: string | null;
|
|
32
|
+
path: string | null;
|
|
33
|
+
route: string;
|
|
34
|
+
host: string | null;
|
|
35
|
+
statusCode: string;
|
|
36
|
+
httpStatus: number | null;
|
|
37
|
+
durationMs: number;
|
|
38
|
+
startTime: string;
|
|
39
|
+
spanCount: number;
|
|
40
|
+
};
|
|
41
|
+
}>;
|
|
42
|
+
/**
|
|
43
|
+
* Returns the full span waterfall for a single trace.
|
|
44
|
+
*/
|
|
45
|
+
getTrace({
|
|
46
|
+
params,
|
|
47
|
+
response,
|
|
48
|
+
serialize
|
|
49
|
+
}: HttpContext): Promise<{
|
|
50
|
+
__response: {
|
|
51
|
+
error: string;
|
|
52
|
+
};
|
|
53
|
+
__status: 404;
|
|
54
|
+
} | {
|
|
55
|
+
data: {
|
|
56
|
+
traceId: string;
|
|
57
|
+
serviceName: string;
|
|
58
|
+
statusCode: string;
|
|
59
|
+
durationMs: number;
|
|
60
|
+
spanId: string;
|
|
61
|
+
parentSpanId: string | null;
|
|
62
|
+
name: string;
|
|
63
|
+
kind: string;
|
|
64
|
+
statusMessage: string;
|
|
65
|
+
startTimeUnix: string;
|
|
66
|
+
endTimeUnix: string;
|
|
67
|
+
events?: any;
|
|
68
|
+
attributes?: any;
|
|
69
|
+
};
|
|
70
|
+
}>;
|
|
71
|
+
/**
|
|
72
|
+
* Lists exceptions extracted from span events.
|
|
73
|
+
*/
|
|
74
|
+
listExceptions({
|
|
75
|
+
request,
|
|
76
|
+
serialize
|
|
77
|
+
}: HttpContext): Promise<{
|
|
78
|
+
data: {
|
|
79
|
+
traceId: string;
|
|
80
|
+
serviceName: string;
|
|
81
|
+
spanId: string;
|
|
82
|
+
spanName: string;
|
|
83
|
+
exceptionType: string;
|
|
84
|
+
exceptionMessage: string;
|
|
85
|
+
exceptionStacktrace: string;
|
|
86
|
+
timestamp: string;
|
|
87
|
+
};
|
|
88
|
+
}>;
|
|
89
|
+
/**
|
|
90
|
+
* Lists database query spans with optional DB system filter.
|
|
91
|
+
*/
|
|
92
|
+
listQueries({
|
|
93
|
+
request,
|
|
94
|
+
serialize
|
|
95
|
+
}: HttpContext): Promise<{
|
|
96
|
+
data: {
|
|
97
|
+
traceId: string;
|
|
98
|
+
dbSystem: string | null;
|
|
99
|
+
serviceName: string;
|
|
100
|
+
statusCode: string;
|
|
101
|
+
durationMs: number;
|
|
102
|
+
startTime: string;
|
|
103
|
+
spanId: string;
|
|
104
|
+
spanName: string;
|
|
105
|
+
dbStatement: string | null;
|
|
106
|
+
dbOperation: string | null;
|
|
107
|
+
};
|
|
108
|
+
}>;
|
|
109
|
+
/**
|
|
110
|
+
* Lists background job traces.
|
|
111
|
+
*/
|
|
112
|
+
listJobs({
|
|
113
|
+
request,
|
|
114
|
+
serialize
|
|
115
|
+
}: HttpContext): Promise<{
|
|
116
|
+
data: {
|
|
117
|
+
traceId: string;
|
|
118
|
+
rootSpanName: string;
|
|
119
|
+
serviceName: string;
|
|
120
|
+
statusCode: string;
|
|
121
|
+
durationMs: number;
|
|
122
|
+
startTime: string;
|
|
123
|
+
spanCount: number;
|
|
124
|
+
jobName: string;
|
|
125
|
+
queueName: string | null;
|
|
126
|
+
};
|
|
127
|
+
}>;
|
|
128
|
+
/**
|
|
129
|
+
* Lists CLI command traces.
|
|
130
|
+
*/
|
|
131
|
+
listCommands({
|
|
132
|
+
request,
|
|
133
|
+
serialize
|
|
134
|
+
}: HttpContext): Promise<{
|
|
135
|
+
data: {
|
|
136
|
+
traceId: string;
|
|
137
|
+
rootSpanName: string;
|
|
138
|
+
serviceName: string;
|
|
139
|
+
statusCode: string;
|
|
140
|
+
durationMs: number;
|
|
141
|
+
startTime: string;
|
|
142
|
+
spanCount: number;
|
|
143
|
+
commandName: string;
|
|
144
|
+
commandDescription: string;
|
|
145
|
+
};
|
|
146
|
+
}>;
|
|
147
|
+
/**
|
|
148
|
+
* Clears all stored telemetry data.
|
|
149
|
+
*/
|
|
150
|
+
clearData({}: HttpContext): Promise<{
|
|
151
|
+
success: boolean;
|
|
152
|
+
}>;
|
|
153
|
+
/**
|
|
154
|
+
* Lists application logs with severity, search, and trace filters.
|
|
155
|
+
*/
|
|
156
|
+
listLogs({
|
|
157
|
+
request,
|
|
158
|
+
serialize
|
|
159
|
+
}: HttpContext): Promise<{
|
|
160
|
+
data: {
|
|
161
|
+
traceId: string | null;
|
|
162
|
+
body: string;
|
|
163
|
+
serviceName: string;
|
|
164
|
+
spanId: string | null;
|
|
165
|
+
timestamp: string;
|
|
166
|
+
timestampUnix: string;
|
|
167
|
+
severityText: string;
|
|
168
|
+
severityNumber: number | null;
|
|
169
|
+
attributes?: any;
|
|
170
|
+
};
|
|
171
|
+
}>;
|
|
172
|
+
}
|
|
173
|
+
//#endregion
|
|
174
|
+
export { ApiController as default };
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { SpanRepository } from "../repositories/span_repository.js";
|
|
2
|
+
import SpanTransformer from "../transformers/span_transformer.js";
|
|
3
|
+
import { LogRepository } from "../repositories/log_repository.js";
|
|
4
|
+
import LogTransformer from "../transformers/log_transformer.js";
|
|
5
|
+
import { listLogsValidator, listQueriesValidator, listTracesValidator, paginationValidator } from "../validators.js";
|
|
6
|
+
import TraceTransformer from "../transformers/trace_transformer.js";
|
|
7
|
+
import QueryTransformer from "../transformers/query_transformer.js";
|
|
8
|
+
import ExceptionTransformer from "../transformers/exception_transformer.js";
|
|
9
|
+
import JobTransformer from "../transformers/job_transformer.js";
|
|
10
|
+
import CommandTransformer from "../transformers/command_transformer.js";
|
|
11
|
+
import "@adonisjs/core/providers/vinejs_provider";
|
|
12
|
+
//#region src/controllers/api_controller.ts
|
|
13
|
+
const spanRepo = new SpanRepository();
|
|
14
|
+
const logRepo = new LogRepository();
|
|
15
|
+
/**
|
|
16
|
+
* HTTP API controller exposing telemetry data collected by the
|
|
17
|
+
* OTLP ingester. Serves paginated lists of traces, logs,
|
|
18
|
+
* exceptions, queries, jobs, and commands.
|
|
19
|
+
*/
|
|
20
|
+
var ApiController = class {
|
|
21
|
+
/**
|
|
22
|
+
* Returns all distinct service names that have emitted spans.
|
|
23
|
+
*/
|
|
24
|
+
async listServices({ response }) {
|
|
25
|
+
const services = await spanRepo.listServices();
|
|
26
|
+
return response.ok({ data: services });
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Lists HTTP request traces with optional service and trace ID filters.
|
|
30
|
+
*/
|
|
31
|
+
async listTraces({ request, serialize }) {
|
|
32
|
+
const { limit = 50, offset = 0, ...filters } = await request.validateUsing(listTracesValidator);
|
|
33
|
+
const rows = await spanRepo.listTraces({
|
|
34
|
+
limit,
|
|
35
|
+
offset,
|
|
36
|
+
...filters
|
|
37
|
+
});
|
|
38
|
+
return { data: await serialize(TraceTransformer.transform(rows)) };
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Returns the full span waterfall for a single trace.
|
|
42
|
+
*/
|
|
43
|
+
async getTrace({ params, response, serialize }) {
|
|
44
|
+
const rows = await spanRepo.getTraceSpans(params.traceId);
|
|
45
|
+
if (!rows.length) return response.notFound({ error: "Trace not found" });
|
|
46
|
+
return { data: await serialize(SpanTransformer.transform(rows)) };
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Lists exceptions extracted from span events.
|
|
50
|
+
*/
|
|
51
|
+
async listExceptions({ request, serialize }) {
|
|
52
|
+
const { limit = 100, offset = 0, ...filters } = await request.validateUsing(paginationValidator);
|
|
53
|
+
const rows = await spanRepo.listExceptions({
|
|
54
|
+
limit,
|
|
55
|
+
offset,
|
|
56
|
+
...filters
|
|
57
|
+
});
|
|
58
|
+
return { data: await serialize(ExceptionTransformer.transform(rows)) };
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Lists database query spans with optional DB system filter.
|
|
62
|
+
*/
|
|
63
|
+
async listQueries({ request, serialize }) {
|
|
64
|
+
const { limit = 100, offset = 0, ...filters } = await request.validateUsing(listQueriesValidator);
|
|
65
|
+
const rows = await spanRepo.listQueries({
|
|
66
|
+
limit,
|
|
67
|
+
offset,
|
|
68
|
+
...filters
|
|
69
|
+
});
|
|
70
|
+
return { data: await serialize(QueryTransformer.transform(rows)) };
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Lists background job traces.
|
|
74
|
+
*/
|
|
75
|
+
async listJobs({ request, serialize }) {
|
|
76
|
+
const { limit = 50, offset = 0, ...filters } = await request.validateUsing(paginationValidator);
|
|
77
|
+
const rows = await spanRepo.listJobs({
|
|
78
|
+
limit,
|
|
79
|
+
offset,
|
|
80
|
+
...filters
|
|
81
|
+
});
|
|
82
|
+
return { data: await serialize(JobTransformer.transform(rows)) };
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Lists CLI command traces.
|
|
86
|
+
*/
|
|
87
|
+
async listCommands({ request, serialize }) {
|
|
88
|
+
const { limit = 50, offset = 0, ...filters } = await request.validateUsing(paginationValidator);
|
|
89
|
+
const rows = await spanRepo.listCommands({
|
|
90
|
+
limit,
|
|
91
|
+
offset,
|
|
92
|
+
...filters
|
|
93
|
+
});
|
|
94
|
+
return { data: await serialize(CommandTransformer.transform(rows)) };
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Clears all stored telemetry data.
|
|
98
|
+
*/
|
|
99
|
+
async clearData({}) {
|
|
100
|
+
await spanRepo.clearAll();
|
|
101
|
+
await logRepo.clearAll();
|
|
102
|
+
return { success: true };
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Lists application logs with severity, search, and trace filters.
|
|
106
|
+
*/
|
|
107
|
+
async listLogs({ request, serialize }) {
|
|
108
|
+
const { limit = 100, offset = 0, ...filters } = await request.validateUsing(listLogsValidator);
|
|
109
|
+
const rows = await logRepo.listLogs({
|
|
110
|
+
limit,
|
|
111
|
+
offset,
|
|
112
|
+
...filters
|
|
113
|
+
});
|
|
114
|
+
return { data: await serialize(LogTransformer.transform(rows)) };
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
//#endregion
|
|
118
|
+
export { ApiController as default };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { HttpContext } from "@adonisjs/core/http";
|
|
2
|
+
|
|
3
|
+
//#region src/controllers/mcp_controller.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Model Context Protocol controller. Creates a stateless MCP server
|
|
6
|
+
* per request with all registered tools, then handles the HTTP
|
|
7
|
+
* streaming transport handshake.
|
|
8
|
+
*/
|
|
9
|
+
declare class McpController {
|
|
10
|
+
/**
|
|
11
|
+
* Handles an incoming MCP HTTP request by spinning up a
|
|
12
|
+
* short-lived MCP server with all devtools tools registered.
|
|
13
|
+
*/
|
|
14
|
+
handle({
|
|
15
|
+
request,
|
|
16
|
+
response
|
|
17
|
+
}: HttpContext): Promise<void>;
|
|
18
|
+
}
|
|
19
|
+
//#endregion
|
|
20
|
+
export { McpController as default };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { registerAllTools } from "../mcp/tools/index.js";
|
|
2
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
4
|
+
//#region src/controllers/mcp_controller.ts
|
|
5
|
+
/**
|
|
6
|
+
* Model Context Protocol controller. Creates a stateless MCP server
|
|
7
|
+
* per request with all registered tools, then handles the HTTP
|
|
8
|
+
* streaming transport handshake.
|
|
9
|
+
*/
|
|
10
|
+
var McpController = class {
|
|
11
|
+
/**
|
|
12
|
+
* Handles an incoming MCP HTTP request by spinning up a
|
|
13
|
+
* short-lived MCP server with all devtools tools registered.
|
|
14
|
+
*/
|
|
15
|
+
async handle({ request, response }) {
|
|
16
|
+
const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: void 0 });
|
|
17
|
+
const server = new McpServer({
|
|
18
|
+
name: "monocle-devtools",
|
|
19
|
+
version: "0.1.0"
|
|
20
|
+
});
|
|
21
|
+
registerAllTools(server);
|
|
22
|
+
await server.connect(transport);
|
|
23
|
+
await transport.handleRequest(request.request, response.response);
|
|
24
|
+
await server.close();
|
|
25
|
+
if (!response.response.writableEnded) response.response.end();
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
//#endregion
|
|
29
|
+
export { McpController as default };
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { HttpContext } from "@adonisjs/core/http";
|
|
2
|
+
|
|
3
|
+
//#region src/controllers/otlp_controller.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* OTLP receiver controller. Accepts OpenTelemetry trace and log
|
|
6
|
+
* exports (JSON + optional gzip), parses them into flat models,
|
|
7
|
+
* persists to DuckDB, and emits real-time events via the EventBus.
|
|
8
|
+
*/
|
|
9
|
+
declare class OtlpController {
|
|
10
|
+
/**
|
|
11
|
+
* Ingests an OTLP trace export request.
|
|
12
|
+
*/
|
|
13
|
+
traces({
|
|
14
|
+
request,
|
|
15
|
+
response
|
|
16
|
+
}: HttpContext): Promise<{
|
|
17
|
+
__response: {
|
|
18
|
+
partialSuccess: {
|
|
19
|
+
rejectedSpans: number;
|
|
20
|
+
errorMessage: string;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
__status: 200;
|
|
24
|
+
} | {
|
|
25
|
+
__response: {
|
|
26
|
+
error: string;
|
|
27
|
+
};
|
|
28
|
+
__status: 400;
|
|
29
|
+
}>;
|
|
30
|
+
/**
|
|
31
|
+
* Ingests an OTLP log export request.
|
|
32
|
+
*/
|
|
33
|
+
logs({
|
|
34
|
+
request,
|
|
35
|
+
response
|
|
36
|
+
}: HttpContext): Promise<{
|
|
37
|
+
__response: {
|
|
38
|
+
partialSuccess: {
|
|
39
|
+
rejectedLogRecords: number;
|
|
40
|
+
errorMessage: string;
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
__status: 200;
|
|
44
|
+
} | {
|
|
45
|
+
__response: {
|
|
46
|
+
error: string;
|
|
47
|
+
};
|
|
48
|
+
__status: 400;
|
|
49
|
+
}>;
|
|
50
|
+
}
|
|
51
|
+
//#endregion
|
|
52
|
+
export { OtlpController as default };
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { SpanRepository } from "../repositories/span_repository.js";
|
|
2
|
+
import { LogRepository } from "../repositories/log_repository.js";
|
|
3
|
+
import { eventBus } from "../services/event_bus.js";
|
|
4
|
+
import { parseLogRequest, parseTraceRequest } from "../services/otlp_parser.js";
|
|
5
|
+
import { gunzipSync } from "node:zlib";
|
|
6
|
+
//#region src/controllers/otlp_controller.ts
|
|
7
|
+
const spanRepo = new SpanRepository();
|
|
8
|
+
const logRepo = new LogRepository();
|
|
9
|
+
/**
|
|
10
|
+
* Reads the full request body into a Buffer with a timeout
|
|
11
|
+
* guard to prevent hung connections.
|
|
12
|
+
*/
|
|
13
|
+
function readRawBody(req, timeoutMs = 5e3) {
|
|
14
|
+
return new Promise((resolve, reject) => {
|
|
15
|
+
const chunks = [];
|
|
16
|
+
const timer = setTimeout(() => {
|
|
17
|
+
req.destroy();
|
|
18
|
+
reject(/* @__PURE__ */ new Error("Body read timeout"));
|
|
19
|
+
}, timeoutMs);
|
|
20
|
+
req.on("data", (chunk) => chunks.push(chunk));
|
|
21
|
+
req.on("end", () => {
|
|
22
|
+
clearTimeout(timer);
|
|
23
|
+
resolve(Buffer.concat(chunks));
|
|
24
|
+
});
|
|
25
|
+
req.on("error", (err) => {
|
|
26
|
+
clearTimeout(timer);
|
|
27
|
+
reject(err);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Reads and decompresses (gzip) an OTLP JSON request body.
|
|
33
|
+
*/
|
|
34
|
+
async function parseOtlpBody(ctx) {
|
|
35
|
+
const raw = await readRawBody(ctx.request.request);
|
|
36
|
+
if (!raw.length) return {};
|
|
37
|
+
const body = ctx.request.header("content-encoding") === "gzip" ? gunzipSync(raw) : raw;
|
|
38
|
+
return JSON.parse(body.toString("utf-8"));
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* OTLP receiver controller. Accepts OpenTelemetry trace and log
|
|
42
|
+
* exports (JSON + optional gzip), parses them into flat models,
|
|
43
|
+
* persists to DuckDB, and emits real-time events via the EventBus.
|
|
44
|
+
*/
|
|
45
|
+
var OtlpController = class {
|
|
46
|
+
/**
|
|
47
|
+
* Ingests an OTLP trace export request.
|
|
48
|
+
*/
|
|
49
|
+
async traces({ request, response }) {
|
|
50
|
+
try {
|
|
51
|
+
const spans = parseTraceRequest(await parseOtlpBody({ request }));
|
|
52
|
+
if (spans.length) {
|
|
53
|
+
await spanRepo.insertSpans(spans);
|
|
54
|
+
eventBus.emitIngested({
|
|
55
|
+
type: "traces",
|
|
56
|
+
count: spans.length
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
return response.ok({ partialSuccess: {
|
|
60
|
+
rejectedSpans: 0,
|
|
61
|
+
errorMessage: ""
|
|
62
|
+
} });
|
|
63
|
+
} catch (error) {
|
|
64
|
+
console.error("[DevTools] Failed to ingest traces:", error);
|
|
65
|
+
return response.badRequest({ error: "Failed to parse OTLP traces" });
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Ingests an OTLP log export request.
|
|
70
|
+
*/
|
|
71
|
+
async logs({ request, response }) {
|
|
72
|
+
try {
|
|
73
|
+
const logs = parseLogRequest(await parseOtlpBody({ request }));
|
|
74
|
+
if (logs.length) {
|
|
75
|
+
await logRepo.insertLogs(logs);
|
|
76
|
+
eventBus.emitIngested({
|
|
77
|
+
type: "logs",
|
|
78
|
+
count: logs.length
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
return response.ok({ partialSuccess: {
|
|
82
|
+
rejectedLogRecords: 0,
|
|
83
|
+
errorMessage: ""
|
|
84
|
+
} });
|
|
85
|
+
} catch (error) {
|
|
86
|
+
console.error("[DevTools] Failed to ingest logs:", error);
|
|
87
|
+
return response.badRequest({ error: "Failed to parse OTLP logs" });
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
//#endregion
|
|
92
|
+
export { OtlpController as default };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { HttpContext } from "@adonisjs/core/http";
|
|
2
|
+
|
|
3
|
+
//#region src/controllers/sse_controller.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Server-Sent Events controller that streams real-time
|
|
6
|
+
* telemetry ingestion events to connected browser clients.
|
|
7
|
+
*/
|
|
8
|
+
declare class SseController {
|
|
9
|
+
/**
|
|
10
|
+
* Establishes a persistent SSE connection and forwards
|
|
11
|
+
* ingested trace/log events until the client disconnects.
|
|
12
|
+
*/
|
|
13
|
+
stream({
|
|
14
|
+
response
|
|
15
|
+
}: HttpContext): Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
//#endregion
|
|
18
|
+
export { SseController as default };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { eventBus } from "../services/event_bus.js";
|
|
2
|
+
//#region src/controllers/sse_controller.ts
|
|
3
|
+
/**
|
|
4
|
+
* Server-Sent Events controller that streams real-time
|
|
5
|
+
* telemetry ingestion events to connected browser clients.
|
|
6
|
+
*/
|
|
7
|
+
var SseController = class {
|
|
8
|
+
/**
|
|
9
|
+
* Establishes a persistent SSE connection and forwards
|
|
10
|
+
* ingested trace/log events until the client disconnects.
|
|
11
|
+
*/
|
|
12
|
+
async stream({ response }) {
|
|
13
|
+
const nodeRes = response.response;
|
|
14
|
+
nodeRes.writeHead(200, {
|
|
15
|
+
"Content-Type": "text/event-stream",
|
|
16
|
+
"Cache-Control": "no-cache",
|
|
17
|
+
Connection: "keep-alive",
|
|
18
|
+
"Access-Control-Allow-Origin": "*"
|
|
19
|
+
});
|
|
20
|
+
nodeRes.write(": connected\n\n");
|
|
21
|
+
const onEvent = (event) => {
|
|
22
|
+
nodeRes.write(`event: ingested\ndata: ${JSON.stringify(event)}\n\n`);
|
|
23
|
+
};
|
|
24
|
+
const unsubscribe = eventBus.onIngested(onEvent);
|
|
25
|
+
/**
|
|
26
|
+
* Keep-alive every 30s to prevent connection timeout.
|
|
27
|
+
*/
|
|
28
|
+
const keepAlive = setInterval(() => nodeRes.write(": ping\n\n"), 3e4);
|
|
29
|
+
nodeRes.on("close", () => {
|
|
30
|
+
unsubscribe();
|
|
31
|
+
clearInterval(keepAlive);
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
//#endregion
|
|
36
|
+
export { SseController as default };
|