Skip to content
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
7 changes: 7 additions & 0 deletions change/apibara-d61e7e81-39c1-43c0-99cd-cfac39828cbe.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "prerelease",
"comment": "feat: add instrumentation for side effects",
"packageName": "apibara",
"email": "jadejajaipal5@gmail.com",
"dependentChangeType": "patch"
}
16 changes: 16 additions & 0 deletions examples/cli/instrumentation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { createLogger } from "apibara/runtime";
import type { LoggerFactoryFn, RegisterFn } from "apibara/types";

export const register: RegisterFn = async () => {
// You can register any side effects such as Opentelemetry, Sentry etc.
};

// You can create a logger with any configuration you want which returns a ConsolaReporter
export const logger: LoggerFactoryFn = ({ indexer, indexers, preset }) => {
// Below logger is just a small customized example of the default logger used by apibara
return createLogger({
indexer: `cli-${indexer}`,
indexers,
preset,
});
};
3 changes: 3 additions & 0 deletions packages/cli/src/rolldown/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import type {
import { serialize } from "../utils/helper";
import { appConfig } from "./plugins/config";
import { indexers } from "./plugins/indexers";
import { instrumentation } from "./plugins/instrumentation";

const runtimeDependencies = [
"better-sqlite3",
Expand Down Expand Up @@ -86,6 +87,8 @@ export function getRolldownConfig(apibara: Apibara): RolldownOptions {
},
}) as RolldownPluginOption,
);

rolldownConfig.plugins?.push(instrumentation(apibara));
rolldownConfig.plugins?.push(indexers(apibara));
rolldownConfig.plugins?.push(appConfig(apibara));

Expand Down
68 changes: 68 additions & 0 deletions packages/cli/src/rolldown/plugins/instrumentation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { existsSync } from "node:fs";
import { join } from "node:path";
import virtual from "@rollup/plugin-virtual";
import type { Apibara } from "apibara/types";
import type { RolldownPluginOption } from "rolldown";

export function instrumentation(apibara: Apibara) {
const instrumentationPath = join(
apibara.options._c12.cwd!,
`instrumentation.${apibara.options._c12.configFile?.endsWith(".ts") ? "ts" : "js"}`,
);

if (!existsSync(instrumentationPath)) {
return virtual({
"#apibara-internal-virtual/instrumentation": `
let register = undefined;
let logger = undefined;

export { register, logger };
`,
}) as RolldownPluginOption;
}

/**
* We are importing the instrumentation file inline with "require" instead of "import" at the top of the file to avoid warnings from rolldown
* when some methods are not defined in the instrumentation file.
*
* Example warning:
*
* [IMPORT_IS_UNDEFINED] Warning: Import `logger` will always be undefined because there is no matching export in 'instrumentation.ts'
* ╭─[virtual:#apibara-internal-virtual/instrumentation:11:35]
* │
* 11 │ if (instrumentation && typeof instrumentation.logger === "function") {
* │ ───────────┬──────────
* │ ╰────────────
* ────╯

* [IMPORT_IS_UNDEFINED] Warning: Import `logger` will always be undefined because there is no matching export in 'instrumentation.ts'
* ╭─[virtual:#apibara-internal-virtual/instrumentation:12:16]
* │
* 12 │ logger = instrumentation.logger;
* │ ───────────┬──────────
* │ ╰────────────
* ────╯
*/
return virtual({
"#apibara-internal-virtual/instrumentation": `
let register = undefined;
let logger = undefined;

try {
const instrumentation = require('${instrumentationPath}');

if (instrumentation?.register && typeof instrumentation.register === "function") {
register = instrumentation.register;
}

if (instrumentation?.logger && typeof instrumentation.logger === "function") {
logger = instrumentation.logger;
}
} catch {
// Silently handle any require errors
}

export { register, logger };
`,
}) as RolldownPluginOption;
}
33 changes: 16 additions & 17 deletions packages/cli/src/runtime/internal/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
import consola from "consola";
import { config } from "#apibara-internal-virtual/config";
import { indexers } from "#apibara-internal-virtual/indexers";
import { logger as instrumentationLogger } from "#apibara-internal-virtual/instrumentation";
import { createLogger } from "./logger";

export const availableIndexers = indexers.map((i) => i.name);
Expand Down Expand Up @@ -56,28 +57,26 @@ export function createIndexer(indexerName: string, preset?: string) {
? indexerModule(runtimeConfig)
: indexerModule;

const reporter: ConsolaReporter = createLogger({
let reporter: ConsolaReporter = createLogger({
indexer: indexerName,
preset,
indexers: availableIndexers,
});

// TODO: Add support for custom logger
//
// Custom logger support is temporarily disabled to prevent bundling issues.
// Previously, when using the config object directly, Rollup would bundle everything
// referenced in apibara.config, including plugins. Now we serialize only the
// essential config properties (runtimeConfig, preset, presets) during build time,
// which prevents unwanted bundling but also means we can't access the logger
// configuration.
//
// if (config.logger) {
// reporter = config.logger({
// indexer: indexerName,
// preset,
// indexers: availableIndexers,
// });
// }
// Check if a custom logger is provided through instrumentation
if (instrumentationLogger) {
// Create a reporter using the custom logger function
const _reporter = instrumentationLogger({
indexer: indexerName,
preset,
indexers: availableIndexers,
});

// If the reporter is valid (has a log method), use it instead of the default
if (_reporter && "log" in _reporter) {
reporter = _reporter;
}
}

// Put the in-memory persistence plugin first so that it can be overridden by any user-defined
// persistence plugin.
Expand Down
7 changes: 7 additions & 0 deletions packages/cli/src/runtime/start.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { runWithReconnect } from "@apibara/indexer";
import { createClient } from "@apibara/protocol";
import { defineCommand, runMain } from "citty";
import consola from "consola";
import { register } from "#apibara-internal-virtual/instrumentation";
import { createIndexer } from "./internal/app";

const startCommand = defineCommand({
Expand Down Expand Up @@ -34,6 +35,12 @@ const startCommand = defineCommand({
indexerInstance.options.streamUrl,
);

if (register) {
consola.start("Registering from instrumentation");
await register();
consola.success("Registered from instrumentation");
}

await runWithReconnect(client, indexerInstance);
},
});
Expand Down
17 changes: 11 additions & 6 deletions packages/cli/src/types/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,19 @@ import type { RolldownOptions } from "rolldown";
import type { DeepPartial } from "./_utils";
import type { ApibaraHooks } from "./hooks";

export type LoggerFactory = ({
export type RegisterFn = () => Promise<void>;

export type LoggerFactoryFn = ({
indexer,
indexers,
preset,
}: { indexer: string; indexers: string[]; preset?: string }) => ConsolaReporter;
}: LoggerFactoryArgs) => ConsolaReporter;

export type LoggerFactoryArgs = {
indexer: string;
indexers: string[];
preset?: string;
};

/**
* Apibara Config type (apibara.config)
Expand All @@ -30,7 +39,6 @@ export interface ApibaraConfig<
runtimeConfig?: R;
presets?: T;
preset?: keyof T;
logger?: LoggerFactory;
}

export type ApibaraDynamicConfig = Pick<ApibaraConfig, "runtimeConfig">;
Expand Down Expand Up @@ -75,9 +83,6 @@ export interface ApibaraOptions<
// Hooks
hooks: NestedHooks<ApibaraHooks>;

// Logging
logger?: LoggerFactory;

// Rolldown
rolldownConfig?: Partial<RolldownOptions>;

Expand Down
4 changes: 4 additions & 0 deletions packages/cli/src/types/virtual/instrumentation.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import type { LoggerFactoryFn, RegisterFn } from "apibara/types";

export const register: RegisterFn | undefined = undefined;
export const logger: LoggerFactoryFn | undefined = undefined;