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

Track project creation count metrics #271

Open
wants to merge 7 commits into
base: development
Choose a base branch
from
Open
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
6 changes: 5 additions & 1 deletion .env
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,8 @@ NEXT_PUBLIC_ENABLE_ADMIN_LOGIN="true"
# Azure AD Variables
AZURE_AD_CLIENT_ID="<azure_ad_client_id>"
AZURE_AD_CLIENT_SECRET="<azure_ad_client_secret>"
AZURE_AD_TENANT_ID="<azure_ad_tenant_id>"
AZURE_AD_TENANT_ID="<azure_ad_tenant_id>"

POSTHOG_HOST="https://us.i.posthog.com"

TELEMETRY_ENABLED="true"
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ WORKDIR /app

ARG LANGTRACE_VERSION

RUN NEXT_PUBLIC_ENABLE_ADMIN_LOGIN=true NEXT_PUBLIC_LANGTRACE_VERSION=$LANGTRACE_VERSION npm run build
RUN POSTHOG_API_KEY=$POSTHOG_API_KEY NEXT_PUBLIC_ENABLE_ADMIN_LOGIN=true NEXT_PUBLIC_LANGTRACE_VERSION=$LANGTRACE_VERSION npm run build

# Final release image
FROM node:21.6-bookworm AS production
Expand Down
15 changes: 14 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ Langtrace.init({ api_key: <your_api_key> })
OR

```typescript
import * as Langtrace from '@langtrase/typescript-sdk'; // Must precede any llm module imports
import * as Langtrace from "@langtrase/typescript-sdk"; // Must precede any llm module imports
LangTrace.init(); // LANGTRACE_API_KEY as an ENVIRONMENT variable
```

Expand Down Expand Up @@ -118,6 +118,19 @@ docker compose down -v

`-v` flag is used to delete volumes

## Telemetry

Langtrace collects basic, non-sensitive usage data from self-hosted instances by default, which is sent to a central server (via PostHog).

This data helps us to:

- Understand how the platform is being used to improve key features.
- Monitor overall usage for internal analysis and reporting.

No sensitive information is gathered, and the data is not shared with third parties.

If you prefer to disable telemetry, you can do so by setting TELEMETRY_ENABLED=false in your configuration.

---

## Supported integrations
Expand Down
13 changes: 8 additions & 5 deletions app/(protected)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { getServerSession } from "next-auth";
import { redirect } from "next/navigation";
import { Suspense } from "react";
import { PageSkeleton } from "./projects/page-client";
import CustomPostHogProvider from "@/components/shared/posthog";

export default async function Layout({
children,
Expand All @@ -19,11 +20,13 @@ export default async function Layout({

return (
<Suspense fallback={<PageLoading />}>
<main className="min-h-screen w-full">
<Header email={session?.user?.email as string} />
<Separator />
{children}
</main>
<CustomPostHogProvider session={session}>
<main className="min-h-screen w-full">
<Header email={session?.user?.email as string} />
<Separator />
{children}
</main>
</CustomPostHogProvider>
</Suspense>
);
}
Expand Down
10 changes: 10 additions & 0 deletions app/api/project/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { authApiKey } from "@/lib/utils";
import { getServerSession } from "next-auth";
import { redirect } from "next/navigation";
import { NextRequest, NextResponse } from "next/server";
import { captureEvent } from "@/lib/services/posthog";

export async function GET(req: NextRequest) {
const session = await getServerSession(authOptions);
Expand Down Expand Up @@ -87,6 +88,15 @@ export async function POST(req: NextRequest) {
},
});
}

const session = await getServerSession(authOptions);
const userEmail = session?.user?.email ?? "anonymous";
await captureEvent(project.id, "project_created", {
project_id: project.id,
project_name: project.name,
project_type: projectType,
created_default_tests: createDefaultTests,
});
}

const { apiKeyHash, ...projectWithoutApiKeyHash } = project;
Expand Down
42 changes: 42 additions & 0 deletions components/shared/posthog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
"use client";

import React, { useEffect, useState } from "react";
import posthog from "posthog-js";
import { PostHogProvider } from "posthog-js/react";
import { Session } from "next-auth";

interface CustomPostHogProviderProps {
children: React.ReactNode;
session: Session | null;
}

export default function CustomPostHogProvider({
children,
session,
}: CustomPostHogProviderProps) {
const [telemetryEnabled, setTelemetryEnabled] = useState<boolean>(false);

useEffect(() => {
async function initializePostHog() {
const enabled = process.env.TELEMETRY_ENABLED === "true";
setTelemetryEnabled(enabled);

if (enabled && typeof window !== "undefined") {
posthog.init(process.env.POSTHOG_API_KEY!, {
api_host: "https://app.posthog.com",
loaded: (posthog) => {
if (process.env.NODE_ENV === "development") posthog.debug();
},
});
}
}

initializePostHog();
}, [session]);

if (!telemetryEnabled) {
return <>{children}</>;
}

return <PostHogProvider client={posthog}>{children}</PostHogProvider>;
}
5 changes: 3 additions & 2 deletions lib/auth/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import { PrismaAdapter } from "@next-auth/prisma-adapter";
import { type NextAuthOptions } from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";
import GoogleProvider from "next-auth/providers/google";
import AzureADProvider from 'next-auth/providers/azure-ad';
import AzureADProvider from "next-auth/providers/azure-ad";
import { captureEvent } from "@/lib/services/posthog";

export const authOptions: NextAuthOptions = {
providers: [
Expand Down Expand Up @@ -64,7 +65,7 @@ export const authOptions: NextAuthOptions = {
clientSecret: process.env.AZURE_AD_CLIENT_SECRET as string,
tenantId: process.env.AZURE_AD_TENANT_ID as string,
allowDangerousEmailAccountLinking: true,
})
}),
],
adapter: PrismaAdapter(prisma),
session: { strategy: "jwt" },
Expand Down
12 changes: 9 additions & 3 deletions lib/middleware/app.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { User } from "@prisma/client";
import { Team, User } from "@prisma/client";
import { getToken } from "next-auth/jwt";
import { NextRequest, NextResponse } from "next/server";
import { captureEvent } from "../services/posthog";

export default async function AppMiddleware(req: NextRequest) {
const path = req.nextUrl.pathname;
Expand Down Expand Up @@ -36,9 +37,10 @@ export default async function AppMiddleware(req: NextRequest) {
const response = await userReq.json();
const user = response.data;
if (user) {
let teamId: string = "";
if (!user.teamId) {
// create a team
await fetch(`${process.env.NEXT_PUBLIC_HOST}/api/team`, {
const team = await fetch(`${process.env.NEXT_PUBLIC_HOST}/api/team`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Expand All @@ -49,8 +51,12 @@ export default async function AppMiddleware(req: NextRequest) {
role: "owner",
status: "active",
}),
});
}).then((res) => res.json());
teamId = team.data.id;
} else {
teamId = user.teamId;
}
await captureEvent(user.id, `user_sign_up-${teamId}`, {});
}

// if there's a session
Expand Down
33 changes: 33 additions & 0 deletions lib/services/posthog.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { PostHog } from "posthog-node";

let posthogClient: PostHog | null = null;

export function getPostHogClient(): PostHog {
if (!posthogClient) {
posthogClient = new PostHog(process.env.POSTHOG_API_KEY!, {
host: process.env.POSTHOG_HOST,
flushAt: 1,
flushInterval: 0,
});
}
return posthogClient;
}

export async function captureEvent(
distinctId: string,
eventName: string,
properties: Record<string, any> = {}
): Promise<void> {
if (process.env.TELEMETRY_ENABLED !== "false") {
const client = getPostHogClient();
await client.capture({
distinctId,
event: eventName,
properties: {
...properties,
is_self_hosted: true,
},
});
await client.shutdown();
}
}
44 changes: 42 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@
"npm": "^10.8.2",
"openai": "^4.40.0",
"pdf-parse": "^1.1.1",
"posthog-js": "^1.161.3",
"posthog-node": "^4.2.0",
"pretty-print-json": "^3.0.0",
"prism": "^1.0.0",
"prismjs": "^1.29.0",
Expand Down