Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

app local metrics #46

Merged
merged 3 commits into from
Jul 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"repository": {
"url": "git+https://github.com/Digital-Alchemy-TS/core"
},
"version": "24.7.1",
"version": "24.7.2",
"author": {
"url": "https://github.com/zoe-codez",
"name": "Zoe Codez"
Expand Down
30 changes: 17 additions & 13 deletions src/extensions/cache.extension.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
import {
CACHE_DELETE_OPERATIONS_TOTAL,
CACHE_DRIVER_ERROR_COUNT,
CACHE_GET_OPERATIONS_TOTAL,
CACHE_SET_OPERATIONS_TOTAL,
createMemoryDriver,
createRedisDriver,
ICacheDriver,
Expand Down Expand Up @@ -36,10 +32,10 @@
`init cache`,
);
if (config.boilerplate.CACHE_PROVIDER === "redis") {
client = await createRedisDriver({ config, lifecycle, logger });
client = await createRedisDriver({ config, internal, lifecycle, logger });

Check warning on line 35 in src/extensions/cache.extension.ts

View check run for this annotation

Codecov / codecov/patch

src/extensions/cache.extension.ts#L35

Added line #L35 was not covered by tests
return;
}
client = await createMemoryDriver({ config, lifecycle, logger });
client = await createMemoryDriver({ config, internal, lifecycle, logger });
});

// #MARK: Return object
Expand All @@ -49,28 +45,32 @@
try {
const fullKey = fullKeyName(key);
await client.del(fullKey);
CACHE_DELETE_OPERATIONS_TOTAL.inc({
internal.boilerplate.metrics.CACHE_DELETE_OPERATIONS_TOTAL.inc({
key: fullKey,
prefix: prefix(),
});
} catch (error) {
CACHE_DRIVER_ERROR_COUNT.labels("del").inc();
internal.boilerplate.metrics.CACHE_DRIVER_ERROR_COUNT.labels(

Check warning on line 53 in src/extensions/cache.extension.ts

View check run for this annotation

Codecov / codecov/patch

src/extensions/cache.extension.ts#L53

Added line #L53 was not covered by tests
"del",
).inc();
logger.error({ error, name: "del" }, `cache error`);
}
},
get: async <T>(key: string, defaultValue?: T): Promise<T> => {
try {
const fullKey = fullKeyName(key);
const result = await client.get(fullKey);
CACHE_GET_OPERATIONS_TOTAL.inc({
internal.boilerplate.metrics.CACHE_GET_OPERATIONS_TOTAL.inc({
hit_miss: is.undefined(result) ? "miss" : "hit",
key: fullKey,
prefix: prefix(),
});
return is.undefined(result) ? defaultValue : (result as T);
} catch (error) {
logger.warn({ defaultValue, error, key, name: "get" }, `cache error`);
CACHE_DRIVER_ERROR_COUNT.labels("get").inc();
internal.boilerplate.metrics.CACHE_DRIVER_ERROR_COUNT.labels(

Check warning on line 71 in src/extensions/cache.extension.ts

View check run for this annotation

Codecov / codecov/patch

src/extensions/cache.extension.ts#L71

Added line #L71 was not covered by tests
"get",
).inc();
return defaultValue;
}
},
Expand All @@ -80,7 +80,9 @@
const keys = await client.keys(fullPattern);
return keys.map((key) => key.slice(Math.max(NONE, prefix().length)));
} catch (error) {
CACHE_DRIVER_ERROR_COUNT.labels("keys").inc();
internal.boilerplate.metrics.CACHE_DRIVER_ERROR_COUNT.labels(

Check warning on line 83 in src/extensions/cache.extension.ts

View check run for this annotation

Codecov / codecov/patch

src/extensions/cache.extension.ts#L83

Added line #L83 was not covered by tests
"keys",
).inc();
logger.warn({ error, name: "keys" }, `cache error`);
return [];
}
Expand All @@ -93,12 +95,14 @@
try {
const fullKey = fullKeyName(key);
await client.set(fullKey, value, ttl);
CACHE_SET_OPERATIONS_TOTAL.inc({
internal.boilerplate.metrics.CACHE_SET_OPERATIONS_TOTAL.inc({
key: fullKey,
prefix: config.boilerplate.CACHE_PREFIX,
});
} catch (error) {
CACHE_DRIVER_ERROR_COUNT.labels("set").inc();
internal.boilerplate.metrics.CACHE_DRIVER_ERROR_COUNT.labels(

Check warning on line 103 in src/extensions/cache.extension.ts

View check run for this annotation

Codecov / codecov/patch

src/extensions/cache.extension.ts#L103

Added line #L103 was not covered by tests
"set",
).inc();
logger.error({ error, name: "set" }, `cache error`);
}
},
Expand Down
29 changes: 21 additions & 8 deletions src/extensions/fetch.extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@
import {
buildFilterString,
DownloadOptions,
FETCH_DOWNLOAD_REQUESTS_SUCCESSFUL,
FETCH_REQUESTS_FAILED,
FETCH_REQUESTS_SUCCESSFUL,
FetchArguments,
FetcherOptions,
FetchProcessTypes,
Expand All @@ -23,7 +20,11 @@

const streamPipeline = promisify(pipeline);

export function Fetch({ logger, context: parentContext }: TServiceParams) {
export function Fetch({
logger,
context: parentContext,
internal,
}: TServiceParams) {
return ({
headers: base_headers,
baseUrl: base_url,
Expand Down Expand Up @@ -111,13 +112,19 @@
try {
const out = await exec();
if (!is.empty(label)) {
FETCH_REQUESTS_SUCCESSFUL.labels(context, label).inc();
internal.boilerplate.metrics.FETCH_REQUESTS_SUCCESSFUL.labels(

Check warning on line 115 in src/extensions/fetch.extension.ts

View check run for this annotation

Codecov / codecov/patch

src/extensions/fetch.extension.ts#L115

Added line #L115 was not covered by tests
context,
label,
).inc();
}
return out;
} catch (error) {
logger.error({ error, name: logContext }, `request failed`);
if (!is.empty(label)) {
FETCH_REQUESTS_FAILED.labels(context, label).inc();
internal.boilerplate.metrics.FETCH_REQUESTS_FAILED.labels(

Check warning on line 124 in src/extensions/fetch.extension.ts

View check run for this annotation

Codecov / codecov/patch

src/extensions/fetch.extension.ts#L124

Added line #L124 was not covered by tests
context,
label,
).inc();
}
throw error;
}
Expand Down Expand Up @@ -148,7 +155,10 @@
});
return await fetchHandleResponse<T>(process, result);
});
FETCH_REQUESTS_SUCCESSFUL.labels(context, label).inc();
internal.boilerplate.metrics.FETCH_REQUESTS_SUCCESSFUL.labels(
context,
label,
).inc();
return out;
}

Expand All @@ -171,7 +181,10 @@
const stream = createWriteStream(destination);
await streamPipeline(response.body, stream);
if (!is.empty(label)) {
FETCH_DOWNLOAD_REQUESTS_SUCCESSFUL.labels(context, label).inc();
internal.boilerplate.metrics.FETCH_DOWNLOAD_REQUESTS_SUCCESSFUL.labels(

Check warning on line 184 in src/extensions/fetch.extension.ts

View check run for this annotation

Codecov / codecov/patch

src/extensions/fetch.extension.ts#L184

Added line #L184 was not covered by tests
context,
label,
).inc();
}
}

Expand Down
1 change: 1 addition & 0 deletions src/extensions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ export * from "./fetch.extension";
export * from "./internal.extension";
export * from "./is.extension";
export * from "./logger.extension";
export * from "./metrics.extension";
export * from "./scheduler.extension";
export * from "./wiring.extension";
2 changes: 1 addition & 1 deletion src/extensions/internal.extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ export class InternalDefinition {
*/
public boilerplate: Pick<
GetApis<typeof LIB_BOILERPLATE>,
"configuration" | "fetch" | "logger"
"configuration" | "fetch" | "logger" | "metrics"
>;
public boot: {
/**
Expand Down
170 changes: 170 additions & 0 deletions src/extensions/metrics.extension.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
/* eslint-disable @typescript-eslint/no-magic-numbers */
import { Counter, Gauge, Histogram, Summary } from "prom-client";

const build = () => {
/**
* Cache delete operations counter
*/
const CACHE_DELETE_OPERATIONS_TOTAL = new Counter({
help: "Total number of cache delete operations",
labelNames: ["prefix", "key"] as const,
name: "digital_alchemy_boilerplate_cache_delete_operations_total",
});

/**
* Cache get operations counter
*/
const CACHE_GET_OPERATIONS_TOTAL = new Counter({
help: "Total number of cache get operations",
labelNames: ["prefix", "key", "hit_miss"] as const,
name: "digital_alchemy_boilerplate_cache_get_operations_total",
});

/**
* Tracks the number of times a scheduled task has been executed.
* Labels:
* - context: The broader category or module the schedule belongs to.
* - label: A user-defined label to identify the specific schedule.
*/
const SCHEDULE_EXECUTION_COUNT = new Counter({
help: "Counts the number of times a scheduled task has been executed",
labelNames: ["context", "label"] as const,
name: "digital_alchemy_boilerplate_schedule_execution_count",
});

/**
* Counts the number of errors occurred during scheduled task executions.
* Labels:
* - context: The broader category or module the schedule belongs to.
* - label: A user-defined label to identify the specific schedule where the error occurred.
*/
const SCHEDULE_ERRORS = new Counter({
help: "Counts the number of errors during scheduled task executions",
labelNames: ["context", "label"] as const,
name: "digital_alchemy_boilerplate_schedule_errors",
});

/**
* Summary for Execution Time
*/
const SCHEDULE_EXECUTION_TIME = new Summary({
help: "Measures the duration of each cron job or interval execution",
labelNames: ["context", "label"] as const,
name: "digital_alchemy_boilerplate_schedule_execution_time",
// These percentiles are just examples; adjust them based on what's relevant for your analysis
percentiles: [0.5, 0.9, 0.99],
});

/**
* Metric to count errors in cache driver
*/
const CACHE_DRIVER_ERROR_COUNT = new Counter({
help: "Counts the number of errors caught in the cache driver",
labelNames: ["methodName"] as const,
name: "digital_alchemy_boilerplate_cache_driver_error_count",
});

/**
* Cache set operations counter
*/
const CACHE_SET_OPERATIONS_TOTAL = new Counter({
help: "Total number of cache set operations",
labelNames: ["prefix", "key"] as const,
name: "digital_alchemy_boilerplate_cache_set_operations_total",
});

/**
* Counts the total number of initiated fetch requests.
*/
const FETCH_REQUESTS_INITIATED = new Counter({
help: "Total number of fetch requests that have been initiated",
name: "digital_alchemy_boilerplate_fetch_requests_initiated_total",
});

/**
* Counts the total number of successfully completed fetch requests.
*/
const FETCH_REQUESTS_SUCCESSFUL = new Counter({
help: "Total number of fetch requests that have been successfully completed",
labelNames: ["context", "label"] as const,
name: "digital_alchemy_boilerplate_fetch_requests_successful_total",
});

/**
* Counts the total number of successfully completed fetch requests.
*/
const FETCH_DOWNLOAD_REQUESTS_SUCCESSFUL = new Counter({
help: "Total number of fetch download requests that have been successfully completed",
labelNames: ["context", "label"] as const,
name: "digital_alchemy_boilerplate_fetch_download_requests_successful_total",
});

/**
* Counts the total number of failed fetch requests.
*/
const FETCH_REQUESTS_FAILED = new Counter({
help: "Total number of fetch requests that have failed",
labelNames: ["context", "label"] as const,
name: "digital_alchemy_boilerplate_fetch_requests_failed_total",
});

/**
* Gauge to count the number of errors encountered in Redis operations.
*/
const REDIS_ERROR_COUNT = new Gauge({
help: "Counts the number of errors encountered in Redis operations",
name: "digital_alchemy_boilerplate_redis_error_count",
});

/**
* Histogram to track the latency of Redis operations in milliseconds.
* Buckets range from 0.1 ms to 1000 ms (1 second) for granular latency measurement.
*/
const REDIS_OPERATION_LATENCY_MS = new Histogram({
buckets: [0.1, 0.5, 1, 5, 10, 20, 50, 100, 200, 500, 1000],
help: "Histogram for tracking the latency of Redis operations in milliseconds",
name: "digital_alchemy_boilerplate_redis_operation_latency_ms",
});

/**
* Counter to track the number of errors encountered in memory cache operations.
*/
const MEMORY_CACHE_ERROR_COUNT = new Counter({
help: "Counts the number of errors encountered in memory cache operations",
name: "digital_alchemy_boilerplate_memory_cache_error_count",
});

/**
* A Prometheus gauge metric that tracks the number of unique context entries in the logger's context cache.
* This helps in monitoring and managing the memory usage associated with the caching of logger contexts.
*/
const LOGGER_CONTEXT_ENTRIES_COUNT = new Gauge({
help: "Number of unique context entries in the logger context cache",
name: "digital_alchemy_boilerplate_logger_context_entries_count",
});

return {
CACHE_DELETE_OPERATIONS_TOTAL,
CACHE_DRIVER_ERROR_COUNT,
CACHE_GET_OPERATIONS_TOTAL,
CACHE_SET_OPERATIONS_TOTAL,
FETCH_DOWNLOAD_REQUESTS_SUCCESSFUL,
FETCH_REQUESTS_FAILED,
FETCH_REQUESTS_INITIATED,
FETCH_REQUESTS_SUCCESSFUL,
LOGGER_CONTEXT_ENTRIES_COUNT,
MEMORY_CACHE_ERROR_COUNT,
REDIS_ERROR_COUNT,
REDIS_OPERATION_LATENCY_MS,
SCHEDULE_ERRORS,
SCHEDULE_EXECUTION_COUNT,
SCHEDULE_EXECUTION_TIME,
};
};

let metrics: ReturnType<typeof build>;

export function Metrics() {
metrics ??= build();
return metrics;
}
Loading
Loading