mirror of
https://github.com/wassname/talk.git
synced 2026-08-05 13:30:26 +08:00
[CORL-281] Metrics (#2298)
* feat: iunitial metrics implementation * fix: graphql endpoint was throwing errors. * feat: add metrics env variables to readme
This commit is contained in:
@@ -1,9 +1,4 @@
|
||||
import {
|
||||
DocumentNode,
|
||||
ExecutionArgs,
|
||||
GraphQLError,
|
||||
OperationDefinitionNode,
|
||||
} from "graphql";
|
||||
import { ExecutionArgs, GraphQLError } from "graphql";
|
||||
import {
|
||||
EndHandler,
|
||||
GraphQLExtension,
|
||||
@@ -12,33 +7,13 @@ import {
|
||||
import now from "performance-now";
|
||||
|
||||
import CommonContext from "talk-server/graph/common/context";
|
||||
import { getOperationMetadata } from "./helpers";
|
||||
|
||||
export function logError(ctx: CommonContext, err: GraphQLError) {
|
||||
ctx.logger.error({ err }, "graphql query error");
|
||||
}
|
||||
|
||||
export class LoggerExtension implements GraphQLExtension<CommonContext> {
|
||||
private getOperationMetadata(doc: DocumentNode) {
|
||||
if (doc.kind === "Document") {
|
||||
const operationDefinition = doc.definitions.find(
|
||||
({ kind }) => kind === "OperationDefinition"
|
||||
) as OperationDefinitionNode | undefined;
|
||||
if (operationDefinition) {
|
||||
let operationName: string | undefined;
|
||||
if (operationDefinition.name) {
|
||||
operationName = operationDefinition.name.value;
|
||||
}
|
||||
|
||||
return {
|
||||
operationName,
|
||||
operation: operationDefinition.operation,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
public executionDidStart(o: {
|
||||
executionArgs: ExecutionArgs;
|
||||
}): EndHandler | void {
|
||||
@@ -55,7 +30,7 @@ export class LoggerExtension implements GraphQLExtension<CommonContext> {
|
||||
o.executionArgs.contextValue.logger.debug(
|
||||
{
|
||||
responseTime,
|
||||
...this.getOperationMetadata(o.executionArgs.document),
|
||||
...getOperationMetadata(o.executionArgs.document),
|
||||
},
|
||||
"graphql query"
|
||||
);
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
import { ExecutionArgs } from "graphql";
|
||||
import { EndHandler, GraphQLExtension } from "graphql-extensions";
|
||||
import now from "performance-now";
|
||||
import { Counter, Histogram } from "prom-client";
|
||||
|
||||
import CommonContext from "talk-server/graph/common/context";
|
||||
import { getOperationMetadata } from "./helpers";
|
||||
|
||||
export interface MetricsExtensionOptions {
|
||||
executedGraphQueriesTotalCounter: Counter;
|
||||
graphQLExecutionTimingsHistogram: Histogram;
|
||||
}
|
||||
|
||||
export class MetricsExtension implements GraphQLExtension<CommonContext> {
|
||||
private options: MetricsExtensionOptions;
|
||||
|
||||
constructor(options: MetricsExtensionOptions) {
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
public executionDidStart(o: {
|
||||
executionArgs: ExecutionArgs;
|
||||
}): EndHandler | void {
|
||||
// Only try to log things if the context is provided.
|
||||
if (o.executionArgs.contextValue) {
|
||||
// Grab the start time so we can calculate the time it takes to execute
|
||||
// the graph query.
|
||||
const startTime = now();
|
||||
return () => {
|
||||
// Compute the end time.
|
||||
const responseTime = Math.round(now() - startTime);
|
||||
|
||||
// Get the request metadata.
|
||||
const { operation, operationName } = getOperationMetadata(
|
||||
o.executionArgs.document
|
||||
);
|
||||
|
||||
if (operation && operationName) {
|
||||
// Increment the graph query value, tagging with the name of the query.
|
||||
this.options.executedGraphQueriesTotalCounter
|
||||
.labels(operation, operationName)
|
||||
.inc();
|
||||
|
||||
this.options.graphQLExecutionTimingsHistogram
|
||||
.labels(operation, operationName)
|
||||
.observe(responseTime);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import { DocumentNode, OperationDefinitionNode } from "graphql";
|
||||
|
||||
export function getOperationMetadata(doc: DocumentNode) {
|
||||
if (doc.kind === "Document") {
|
||||
const operationDefinition = doc.definitions.find(
|
||||
({ kind }) => kind === "OperationDefinition"
|
||||
) as OperationDefinitionNode | undefined;
|
||||
if (operationDefinition) {
|
||||
let operationName: string | undefined;
|
||||
if (operationDefinition.name) {
|
||||
operationName = operationDefinition.name.value;
|
||||
}
|
||||
|
||||
return {
|
||||
operationName,
|
||||
operation: operationDefinition.operation,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
@@ -1,2 +1,3 @@
|
||||
export * from "./ErrorWrappingExtension";
|
||||
export * from "./LoggerExtension";
|
||||
export * from "./MetricsExtension";
|
||||
|
||||
Reference in New Issue
Block a user