Skip to content
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
11 changes: 11 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,17 @@ AZURE_TENANT_ID=
AZURE_SUBSCRIPTION_ID=
AZURE_STORAGE_CONNECTION_STRING=

# Application Insights (for debug log access)
APPINSIGHTS_APP_ID=
APPINSIGHTS_API_KEY=
# Optional: Additional container apps (for multi-app log access)
APPINSIGHTS_JUICEDOLLAR_API_APP_ID=
APPINSIGHTS_DEURO_API_APP_ID=
APPINSIGHTS_DEURO_MONITORING_APP_ID=
APPINSIGHTS_JUICESWAP_PONDER_APP_ID=
APPINSIGHTS_DEURO_PONDER_APP_ID=
APPINSIGHTS_REALUNIT_PONDER_APP_ID=

FIXER_BASE_URL=
FIXER_API_KEY=

Expand Down
45 changes: 39 additions & 6 deletions scripts/log-debug.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,16 @@
#
# Options:
# -h, --hours <n> Time range in hours (default: 1, max: 168)
# -a, --app <name> Container app to query (default: dfxApi)
#
# Available apps:
# dfxApi DFX API (default)
# juicedollarApi JuiceDollar API
# deuroApi dEuro API
# deuroMonitoring dEuro Monitoring
# juiceswapPonder JuiceSwap Ponder
# deuroPonder dEuro Ponder
# realunitPonder RealUnit Ponder
#
# Environment:
# Copy .env.db-debug.sample to .env.db-debug and fill in your credentials
Expand Down Expand Up @@ -41,6 +51,7 @@ API_URL="${DEBUG_API_URL:-https://api.dfx.swiss/v1}"

# Parse arguments
HOURS=1
APP=""
COMMAND="${1:-exceptions}"
shift 2>/dev/null || true

Expand All @@ -51,6 +62,10 @@ while [[ $# -gt 0 ]]; do
HOURS="$2"
shift 2
;;
-a|--app)
APP="$2"
shift 2
;;
*)
PARAM="$1"
shift
Expand All @@ -76,23 +91,26 @@ ROLE=$(echo "$TOKEN" | cut -d'.' -f2 | base64 -d 2>/dev/null | jq -r '.role' 2>/
echo "Authenticated with role: $ROLE"
echo ""

# Build app suffix for display
APP_DISPLAY="${APP:-dfxApi}"

# Build request based on command
case $COMMAND in
exceptions|exc)
TEMPLATE="exceptions-recent"
BODY="{\"template\":\"$TEMPLATE\",\"hours\":$HOURS}"
echo "=== Recent Exceptions (last ${HOURS}h) ==="
echo "=== Recent Exceptions [$APP_DISPLAY] (last ${HOURS}h) ==="
;;
failures|fail)
TEMPLATE="request-failures"
BODY="{\"template\":\"$TEMPLATE\",\"hours\":$HOURS}"
echo "=== Failed Requests (last ${HOURS}h) ==="
echo "=== Failed Requests [$APP_DISPLAY] (last ${HOURS}h) ==="
;;
slow)
TEMPLATE="dependencies-slow"
DURATION="${PARAM:-1000}"
BODY="{\"template\":\"$TEMPLATE\",\"hours\":$HOURS,\"durationMs\":$DURATION}"
echo "=== Slow Dependencies >${DURATION}ms (last ${HOURS}h) ==="
echo "=== Slow Dependencies >${DURATION}ms [$APP_DISPLAY] (last ${HOURS}h) ==="
;;
traces|trace)
if [ -z "$PARAM" ]; then
Expand All @@ -102,7 +120,7 @@ case $COMMAND in
fi
TEMPLATE="traces-by-message"
BODY="{\"template\":\"$TEMPLATE\",\"hours\":$HOURS,\"messageFilter\":\"$PARAM\"}"
echo "=== Traces containing '$PARAM' (last ${HOURS}h) ==="
echo "=== Traces containing '$PARAM' [$APP_DISPLAY] (last ${HOURS}h) ==="
;;
operation|op)
if [ -z "$PARAM" ]; then
Expand All @@ -112,7 +130,7 @@ case $COMMAND in
fi
TEMPLATE="traces-by-operation"
BODY="{\"template\":\"$TEMPLATE\",\"hours\":$HOURS,\"operationId\":\"$PARAM\"}"
echo "=== Traces for operation $PARAM (last ${HOURS}h) ==="
echo "=== Traces for operation $PARAM [$APP_DISPLAY] (last ${HOURS}h) ==="
;;
events|event)
if [ -z "$PARAM" ]; then
Expand All @@ -122,7 +140,7 @@ case $COMMAND in
fi
TEMPLATE="custom-events"
BODY="{\"template\":\"$TEMPLATE\",\"hours\":$HOURS,\"eventName\":\"$PARAM\"}"
echo "=== Custom Events '$PARAM' (last ${HOURS}h) ==="
echo "=== Custom Events '$PARAM' [$APP_DISPLAY] (last ${HOURS}h) ==="
;;
*)
echo "Unknown command: $COMMAND"
Expand All @@ -134,10 +152,25 @@ case $COMMAND in
echo " traces <msg> Search trace messages"
echo " operation <id> Traces by operation GUID"
echo " events <name> Custom events by name"
echo ""
echo "Available apps (-a, --app):"
echo " dfxApi DFX API (default)"
echo " juicedollarApi JuiceDollar API"
echo " deuroApi dEuro API"
echo " deuroMonitoring dEuro Monitoring"
echo " juiceswapPonder JuiceSwap Ponder"
echo " deuroPonder dEuro Ponder"
echo " realunitPonder RealUnit Ponder"
exit 1
;;
esac

# Add app parameter to body if specified
if [ -n "$APP" ]; then
# Remove trailing } and add app parameter
BODY="${BODY%\}},\"app\":\"$APP\"}"
fi

echo ""

# Execute log query
Expand Down
9 changes: 9 additions & 0 deletions src/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1018,6 +1018,15 @@ export class Configuration {
appInsights: {
appId: process.env.APPINSIGHTS_APP_ID,
apiKey: process.env.APPINSIGHTS_API_KEY,
apps: {
dfxApi: process.env.APPINSIGHTS_APP_ID,
juicedollarApi: process.env.APPINSIGHTS_JUICEDOLLAR_API_APP_ID,
deuroApi: process.env.APPINSIGHTS_DEURO_API_APP_ID,
deuroMonitoring: process.env.APPINSIGHTS_DEURO_MONITORING_APP_ID,
juiceswapPonder: process.env.APPINSIGHTS_JUICESWAP_PONDER_APP_ID,
deuroPonder: process.env.APPINSIGHTS_DEURO_PONDER_APP_ID,
realunitPonder: process.env.APPINSIGHTS_REALUNIT_PONDER_APP_ID,
},
},
};

Expand Down
10 changes: 7 additions & 3 deletions src/integration/infrastructure/app-insights-query.service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Injectable } from '@nestjs/common';
import { Config } from 'src/config/config';
import { HttpService } from 'src/shared/services/http.service';
import { ContainerApp } from './enums/container-app.enum';

interface AppInsightsQueryResponse {
tables: {
Expand All @@ -16,11 +17,14 @@ export class AppInsightsQueryService {

constructor(private readonly http: HttpService) {}

async query(kql: string, timespan?: string): Promise<AppInsightsQueryResponse> {
const { appId, apiKey } = Config.azure.appInsights;
async query(kql: string, timespan?: string, app?: ContainerApp): Promise<AppInsightsQueryResponse> {
const { appId: defaultAppId, apiKey, apps } = Config.azure.appInsights;

// Use specified app or default to dfxApi
const appId = app ? apps[app] : defaultAppId;

if (!appId || !apiKey) {
throw new Error('App insights config missing');
throw new Error(app ? `App insights config missing for ${app}` : 'App insights config missing');
}

const body: { query: string; timespan?: string } = { query: kql };
Expand Down
9 changes: 9 additions & 0 deletions src/integration/infrastructure/enums/container-app.enum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export enum ContainerApp {
DFX_API = 'dfxApi',
JUICEDOLLAR_API = 'juicedollarApi',
DEURO_API = 'deuroApi',
DEURO_MONITORING = 'deuroMonitoring',
JUICESWAP_PONDER = 'juiceswapPonder',
DEURO_PONDER = 'deuroPonder',
REALUNIT_PONDER = 'realunitPonder',
}
7 changes: 7 additions & 0 deletions src/subdomains/generic/gs/dto/log-query.dto.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { IsEnum, IsInt, IsOptional, IsString, Matches, Max, Min } from 'class-validator';
import { ContainerApp } from 'src/integration/infrastructure/enums/container-app.enum';

export { ContainerApp };

export enum LogQueryTemplate {
TRACES_BY_OPERATION = 'traces-by-operation',
Expand All @@ -13,6 +16,10 @@ export class LogQueryDto {
@IsEnum(LogQueryTemplate)
template: LogQueryTemplate;

@IsOptional()
@IsEnum(ContainerApp)
app?: ContainerApp;

@IsOptional()
@IsString()
@Matches(/^[a-f0-9-]{36}$/i, { message: 'operationId must be a valid GUID' })
Expand Down
6 changes: 4 additions & 2 deletions src/subdomains/generic/gs/gs.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -285,13 +285,15 @@ export class GsService {
kql += `\n| take ${template.defaultLimit}`;

// Log for audit
this.logger.verbose(`Log query by ${userIdentifier}: template=${dto.template}, params=${JSON.stringify(dto)}`);
this.logger.verbose(
`Log query by ${userIdentifier}: template=${dto.template}, app=${dto.app ?? 'dfxApi'}, params=${JSON.stringify(dto)}`,
);

// Execute
const timespan = `PT${dto.hours ?? 1}H`;

try {
const response = await this.appInsightsQueryService.query(kql, timespan);
const response = await this.appInsightsQueryService.query(kql, timespan, dto.app);

if (!response.tables?.length) {
return { columns: [], rows: [] };
Expand Down
Loading