Skip to content

Commit fc33f26

Browse files
authored
add grafana alloy config and setup (#210)
* add grafana alloy config and setup * add basic repo prom metrics * nits in dockerfile
1 parent 04f6772 commit fc33f26

File tree

8 files changed

+564
-30
lines changed

8 files changed

+564
-30
lines changed

Dockerfile

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -111,9 +111,6 @@ ENV POSTHOG_PAPIK=$POSTHOG_PAPIK
111111

112112
ENV STRIPE_PUBLISHABLE_KEY=""
113113

114-
# Configure dependencies
115-
RUN apk add --no-cache git ca-certificates bind-tools tini jansson wget supervisor uuidgen curl perl jq redis postgresql postgresql-contrib openssl util-linux
116-
117114
# Configure zoekt
118115
COPY vendor/zoekt/install-ctags-alpine.sh .
119116
RUN ./install-ctags-alpine.sh && rm install-ctags-alpine.sh
@@ -144,6 +141,19 @@ COPY --from=shared-libs-builder /app/packages/schemas ./packages/schemas
144141
COPY --from=shared-libs-builder /app/packages/crypto ./packages/crypto
145142
COPY --from=shared-libs-builder /app/packages/error ./packages/error
146143

144+
# Configure dependencies
145+
RUN apk add --no-cache git ca-certificates bind-tools tini jansson wget supervisor uuidgen curl perl jq redis postgresql postgresql-contrib openssl util-linux unzip
146+
147+
# Install grafana alloy. libc6-compat is required because alloy dynamically links against glibc which doesn't exist in alpine by default
148+
# @nochekin: figure out how to handle this for self hosted case (especially the config)
149+
RUN apk add --no-cache libc6-compat
150+
RUN wget https://github.com/grafana/alloy/releases/download/v1.7.0/alloy-linux-amd64.zip \
151+
&& unzip alloy-linux-amd64.zip \
152+
&& mv alloy-linux-amd64 /usr/local/bin/alloy \
153+
&& chmod +x /usr/local/bin/alloy \
154+
&& rm alloy-linux-amd64.zip
155+
COPY grafana.alloy .
156+
147157
# Configure the database
148158
RUN mkdir -p /run/postgresql && \
149159
chown -R postgres:postgres /run/postgresql && \
@@ -157,7 +167,6 @@ RUN chmod +x ./entrypoint.sh
157167

158168
COPY default-config.json .
159169

160-
161170
EXPOSE 3000
162171
ENV PORT=3000
163172
ENV HOSTNAME="0.0.0.0"

grafana.alloy

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
prometheus.scrape "local_app" {
2+
targets = [
3+
{
4+
__address__ = "localhost:6070",
5+
},
6+
{
7+
__address__ = "localhost:3060",
8+
},
9+
]
10+
11+
metrics_path = "/metrics"
12+
scrape_interval = "10s"
13+
14+
job_name = sys.env("GRAFANA_ENVIRONMENT")
15+
16+
forward_to = [
17+
prometheus.remote_write.grafana_cloud.receiver,
18+
]
19+
}
20+
21+
prometheus.remote_write "grafana_cloud" {
22+
endpoint {
23+
url = sys.env("GRAFANA_ENDPOINT")
24+
25+
basic_auth {
26+
username = sys.env("GRAFANA_USERNAME")
27+
password = sys.env("GRAFANA_PASSWORD")
28+
}
29+
}
30+
}

packages/backend/package.json

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,22 +23,25 @@
2323
"dependencies": {
2424
"@gitbeaker/rest": "^40.5.1",
2525
"@octokit/rest": "^21.0.2",
26+
"@sourcebot/crypto": "^0.1.0",
27+
"@sourcebot/db": "^0.1.0",
28+
"@sourcebot/error": "^0.1.0",
29+
"@sourcebot/schemas": "^0.1.0",
30+
"@types/express": "^5.0.0",
2631
"argparse": "^2.0.1",
32+
"bullmq": "^5.34.10",
2733
"cross-fetch": "^4.0.0",
2834
"dotenv": "^16.4.5",
35+
"express": "^4.21.2",
2936
"gitea-js": "^1.22.0",
3037
"glob": "^11.0.0",
38+
"ioredis": "^5.4.2",
3139
"lowdb": "^7.0.1",
3240
"micromatch": "^4.0.8",
3341
"posthog-node": "^4.2.1",
34-
"@sourcebot/crypto": "^0.1.0",
35-
"@sourcebot/db": "^0.1.0",
36-
"@sourcebot/schemas": "^0.1.0",
37-
"@sourcebot/error": "^0.1.0",
42+
"prom-client": "^15.1.3",
3843
"simple-git": "^3.27.0",
3944
"strip-json-comments": "^5.0.1",
40-
"winston": "^3.15.0",
41-
"bullmq": "^5.34.10",
42-
"ioredis": "^5.4.2"
45+
"winston": "^3.15.0"
4346
}
4447
}

packages/backend/src/main.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { Redis } from 'ioredis';
66
import { ConnectionManager } from './connectionManager.js';
77
import { RepoManager } from './repoManager.js';
88
import { INDEX_CONCURRENCY_MULTIPLE, REDIS_URL } from './environment.js';
9+
import { PromClient } from './promClient.js';
910

1011
const logger = createLogger('main');
1112

@@ -26,9 +27,11 @@ export const main = async (db: PrismaClient, context: AppContext) => {
2627
settings.indexConcurrencyMultiple = parseInt(INDEX_CONCURRENCY_MULTIPLE);
2728
}
2829

30+
const promClient = new PromClient();
31+
2932
const connectionManager = new ConnectionManager(db, settings, redis);
3033
connectionManager.registerPollingCallback();
3134

32-
const repoManager = new RepoManager(db, settings, redis, context);
35+
const repoManager = new RepoManager(db, settings, redis, promClient, context);
3336
await repoManager.blockingPollLoop();
3437
}

packages/backend/src/promClient.ts

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import express, { Request, Response } from 'express';
2+
import client, { Registry, Counter, Gauge, Histogram } from 'prom-client';
3+
4+
export class PromClient {
5+
private registry: Registry;
6+
private app: express.Application;
7+
public activeRepoIndexingJobs: Gauge<string>;
8+
public repoIndexingDuration: Histogram<string>;
9+
public repoIndexingErrors: Counter<string>;
10+
public repoIndexingFails: Counter<string>;
11+
public repoIndexingSuccesses: Counter<string>;
12+
public readonly PORT = 3060;
13+
14+
constructor() {
15+
this.registry = new Registry();
16+
17+
this.activeRepoIndexingJobs = new Gauge({
18+
name: 'active_repo_indexing_jobs',
19+
help: 'The number of repo indexing jobs in progress',
20+
labelNames: ['repo'],
21+
});
22+
this.registry.registerMetric(this.activeRepoIndexingJobs);
23+
24+
this.repoIndexingDuration = new Histogram({
25+
name: 'repo_indexing_duration',
26+
help: 'The duration of repo indexing jobs',
27+
labelNames: ['repo'],
28+
});
29+
this.registry.registerMetric(this.repoIndexingDuration);
30+
31+
this.repoIndexingErrors = new Counter({
32+
name: 'repo_indexing_errors',
33+
help: 'The number of repo indexing errors',
34+
labelNames: ['repo'],
35+
});
36+
this.registry.registerMetric(this.repoIndexingErrors);
37+
38+
this.repoIndexingFails = new Counter({
39+
name: 'repo_indexing_fails',
40+
help: 'The number of repo indexing fails',
41+
labelNames: ['repo'],
42+
});
43+
this.registry.registerMetric(this.repoIndexingFails);
44+
45+
this.repoIndexingSuccesses = new Counter({
46+
name: 'repo_indexing_successes',
47+
help: 'The number of repo indexing successes',
48+
labelNames: ['repo'],
49+
});
50+
this.registry.registerMetric(this.repoIndexingSuccesses);
51+
52+
client.collectDefaultMetrics({
53+
register: this.registry,
54+
});
55+
56+
this.app = express();
57+
this.app.get('/metrics', async (req: Request, res: Response) => {
58+
res.set('Content-Type', this.registry.contentType);
59+
60+
const metrics = await this.registry.metrics();
61+
res.end(metrics);
62+
});
63+
64+
this.app.listen(this.PORT, () => {
65+
console.log(`Prometheus metrics server is running on port ${this.PORT}`);
66+
});
67+
}
68+
69+
getRegistry(): Registry {
70+
return this.registry;
71+
}
72+
}

packages/backend/src/repoManager.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,12 @@ import { createLogger } from "./logger.js";
44
import { Connection, PrismaClient, Repo, RepoToConnection, RepoIndexingStatus, StripeSubscriptionStatus } from "@sourcebot/db";
55
import { GithubConnectionConfig, GitlabConnectionConfig, GiteaConnectionConfig } from '@sourcebot/schemas/v3/connection.type';
66
import { AppContext, Settings } from "./types.js";
7-
import { captureEvent } from "./posthog.js";
87
import { getRepoPath, getTokenFromConfig, measure, getShardPrefix } from "./utils.js";
98
import { cloneRepository, fetchRepository } from "./git.js";
109
import { existsSync, rmSync, readdirSync } from 'fs';
1110
import { indexGitRepository } from "./zoekt.js";
1211
import os from 'os';
13-
import { BackendException } from "@sourcebot/error";
12+
import { PromClient } from './promClient.js';
1413

1514
interface IRepoManager {
1615
blockingPollLoop: () => void;
@@ -34,6 +33,7 @@ export class RepoManager implements IRepoManager {
3433
private db: PrismaClient,
3534
private settings: Settings,
3635
redis: Redis,
36+
private promClient: PromClient,
3737
private ctx: AppContext,
3838
) {
3939
this.queue = new Queue<JobPayload>(QUEUE_NAME, {
@@ -280,6 +280,7 @@ export class RepoManager implements IRepoManager {
280280
repoIndexingStatus: RepoIndexingStatus.INDEXING,
281281
}
282282
});
283+
this.promClient.activeRepoIndexingJobs.inc();
283284

284285
let indexDuration_s: number | undefined;
285286
let fetchDuration_s: number | undefined;
@@ -295,6 +296,7 @@ export class RepoManager implements IRepoManager {
295296
break;
296297
} catch (error) {
297298
attempts++;
299+
this.promClient.repoIndexingErrors.inc();
298300
if (attempts === maxAttempts) {
299301
this.logger.error(`Failed to sync repository ${repo.id} after ${maxAttempts} attempts. Error: ${error}`);
300302
throw error;
@@ -313,7 +315,9 @@ export class RepoManager implements IRepoManager {
313315

314316
private async onIndexJobCompleted(job: Job<JobPayload>) {
315317
this.logger.info(`Repo index job ${job.id} completed`);
316-
318+
this.promClient.activeRepoIndexingJobs.dec();
319+
this.promClient.repoIndexingSuccesses.inc();
320+
317321
await this.db.repo.update({
318322
where: {
319323
id: job.data.repo.id,
@@ -328,6 +332,9 @@ export class RepoManager implements IRepoManager {
328332
private async onIndexJobFailed(job: Job<JobPayload> | undefined, err: unknown) {
329333
this.logger.info(`Repo index job failed (id: ${job?.id ?? 'unknown'}) with error: ${err}`);
330334
if (job) {
335+
this.promClient.activeRepoIndexingJobs.dec();
336+
this.promClient.repoIndexingFails.inc();
337+
331338
await this.db.repo.update({
332339
where: {
333340
id: job.data.repo.id,

supervisord.conf

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,13 @@ autorestart=true
3737
startretries=3
3838
stdout_logfile=/dev/fd/1
3939
stdout_logfile_maxbytes=0
40+
redirect_stderr=true
41+
42+
[program:alloy]
43+
command=alloy run grafana.alloy
44+
autostart=true
45+
autorestart=true
46+
startretries=3
47+
stdout_logfile=/dev/fd/1
48+
stdout_logfile_maxbytes=0
4049
redirect_stderr=true

0 commit comments

Comments
 (0)