evanderkoogh / otel-cf-workers

An OpenTelemetry compatible library for instrumenting and exporting traces for Cloudflare Workers
BSD 3-Clause "New" or "Revised" License
208 stars 39 forks source link

getActiveConfig() returns null #76

Closed philippviereck closed 2 months ago

philippviereck commented 7 months ago

https://github.com/evanderkoogh/otel-cf-workers/blob/2d370cb9098a9066da3ba2b3102d6f29e5c99c31/src/config.ts#L12

I use SvelteKit on Cloudflare workers. I modified the SvelteKit Workers Adapter (this file) such that it instruments the the fetch handler using otel-cf-workers.

The worker-file where SvelteKit gets 'injected' looks like this then.

import { Server } from 'SERVER';
import { manifest, prerendered } from 'MANIFEST';
import { getAssetFromKV, mapRequestToAsset } from '@cloudflare/kv-asset-handler';
import static_asset_manifest_json from '__STATIC_CONTENT_MANIFEST';
const static_asset_manifest = JSON.parse(static_asset_manifest_json);
import { instrument } from '../../custom-otel-cf-workers/src/index.js';

const server = new Server(manifest);

const app_path = `/${manifest.appPath}/`;

const handler = {
    /**
     * @param {Request} req
     * @param {any} env
     * @param {any} context
     */
    async fetch(req, env, context) {
        await server.init({ env });

        const url = new URL(req.url);

        // static assets
        if (url.pathname.startsWith(app_path)) {
            /** @type {Response} */
            const res = await get_asset_from_kv(req, env, context);
            if (is_error(res.status)) return res;

            const cache_control = url.pathname.startsWith(app_path + 'immutable/')
                ? 'public, immutable, max-age=31536000'
                : 'no-cache';

            return new Response(res.body, {
                headers: {
                    // include original headers, minus cache-control which
                    // is overridden, and etag which is no longer useful
                    'cache-control': cache_control,
                    'content-type': res.headers.get('content-type'),
                    'x-robots-tag': 'noindex'
                }
            });
        }

        let { pathname, search } = url;
        try {
            pathname = decodeURIComponent(pathname);
        } catch {
            // ignore invalid URI
        }

        const stripped_pathname = pathname.replace(/\/$/, '');

        // prerendered pages and /static files
        let is_static_asset = false;
        const filename = stripped_pathname.substring(1);
        if (filename) {
            is_static_asset =
                manifest.assets.has(filename) || manifest.assets.has(filename + '/index.html');
        }

        let location = pathname.at(-1) === '/' ? stripped_pathname : pathname + '/';

        if (is_static_asset || prerendered.has(pathname)) {
            return get_asset_from_kv(req, env, context, (request, options) => {
                if (prerendered.has(pathname)) {
                    url.pathname = '/' + prerendered.get(pathname).file;
                    return new Request(url.toString(), request);
                }

                return mapRequestToAsset(request, options);
            });
        } else if (location && prerendered.has(location)) {
            if (search) location += search;
            return new Response('', {
                status: 308,
                headers: {
                    location
                }
            });
        }

        // dynamically-generated pages
        return await server.respond(req, {
            platform: {
                env,
                context,
                // @ts-expect-error lib.dom is interfering with workers-types
                caches,
                // @ts-expect-error req is actually a Cloudflare request not a standard request
                cf: req.cf
            },
            getClientAddress() {
                return req.headers.get('cf-connecting-ip');
            }
        });
    }
};

/**
 * @param {Request} req
 * @param {any} env
 * @param {any} context
 */
async function get_asset_from_kv(req, env, context, map = mapRequestToAsset) {
    return await getAssetFromKV(
        {
            request: req,
            waitUntil(promise) {
                return context.waitUntil(promise);
            }
        },
        {
            ASSET_NAMESPACE: env.__STATIC_CONTENT,
            ASSET_MANIFEST: static_asset_manifest,
            mapRequestToAsset: map
        }
    );
}

/**
 * @param {number} status
 * @returns {boolean}
 */
function is_error(status) {
    return status > 399;
}

const config = (
    /** @type {{ BASELIME_API_KEY: any; SERVICE_NAME: any; }} */ env,
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    /** @type {any} */ _trigger
) => {
    return {
        exporter: {
            url: 'https://otel.baselime.io/v1',
            headers: { 'x-api-key': env.BASELIME_API_KEY }
        },
        service: { name: env.SERVICE_NAME }
    };
};

export default instrument(handler, config);
philippviereck commented 7 months ago

With these "patches" I can run the worker: https://github.com/philippviereck/otel-cf-workers/tree/debugging

Seems to crash when doing a POST to turso. Could it be something with the fetch() of https://www.npmjs.com/package/@libsql/client ?

mehulmathur16 commented 4 months ago

Hi @philippviereck, were you able to fix this ?

philippviereck commented 4 months ago

@mehulmathur16 no, I was not. Haven't had the time so far...

philippviereck commented 4 months ago

It never crashed on me so far but those are really just VERY DIRTY patches to catch uncaught errors and give hints as to where the critical sections are... I did not put any thought into it, I just wanted the crashes to go away.

mehulmathur16 commented 4 months ago

Okay got it, thanks.

mehulmathur16 commented 4 months ago

@philippviereck this issue is temporarily fixed in https://github.com/mehulmathur16/otel-cf-workers

Ankcorn commented 4 months ago

These patches don't quite get to the root cause of the issue, and will lead to spans being created not in the tree so traces will be broken.

For some reason for some fetch calls AsyncLocalStorage looses the context.

There are a few github issues about this but none of them have a clear fix for this problem

https://github.com/nodejs/node/issues/41285

In the scenario I am looking into this for it works for planetscale database fetches, but not when that planetscale fetch is passed into drizzle-orm. I'd suggest something similar is happening with the turso client.

philippviereck commented 4 months ago

Should be solved by: https://github.com/cloudflare/workerd/issues/1513#issuecomment-1954456751