Skip to content

Instantly share code, notes, and snippets.

@kudoh
Last active May 31, 2024 01:48
Show Gist options
  • Select an option

  • Save kudoh/ccc1ab931cc2fa636c28f4b3bccca7a2 to your computer and use it in GitHub Desktop.

Select an option

Save kudoh/ccc1ab931cc2fa636c28f4b3bccca7a2 to your computer and use it in GitHub Desktop.
x-ray custom trace collector sample
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs';
import * as iam from 'aws-cdk-lib/aws-iam';
import { Effect } from 'aws-cdk-lib/aws-iam';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as apigw from 'aws-cdk-lib/aws-apigatewayv2';
import { CorsHttpMethod } from 'aws-cdk-lib/aws-apigatewayv2';
import { RetentionDays } from 'aws-cdk-lib/aws-logs';
import * as integrations from 'aws-cdk-lib/aws-apigatewayv2-integrations';
export class CustomXrayStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const role = new iam.Role(this, 'GptSlackGatewayLambdaRole', {
roleName: `${this.stackName}-trace-role`,
assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'),
managedPolicies: [iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaBasicExecutionRole')],
inlinePolicies: {
XRayPutCustomSegment: new iam.PolicyDocument({
statements: [new iam.PolicyStatement({
effect: Effect.ALLOW,
resources: ["*"],
actions: ['xray:PutTraceSegments']
})]
}),
}
});
const lambdaFn = new NodejsFunction(this, 'XRayCustomTrace', {
role: role,
functionName: `${this.stackName}-custom-xray-trace`,
description: 'custom xray entry point',
entry: 'lambda/handler.ts',
handler: 'handler',
runtime: lambda.Runtime.NODEJS_20_X,
logRetention: RetentionDays.ONE_WEEK,
})
const httpApi = new apigw.HttpApi(this, 'HttpApi', {
corsPreflight: {
allowHeaders: ["*"],
allowMethods: [CorsHttpMethod.ANY],
allowOrigins: ["*"]
}
});
httpApi.addRoutes({
path: '/',
methods: [apigw.HttpMethod.POST],
integration: new integrations.HttpLambdaIntegration('Integration', lambdaFn)
});
new cdk.CfnOutput(this, 'HttpApiUrl', {
value: httpApi.url!,
description: 'The url of the HttpApi',
exportName: `${this.stackName}-http-api-url`,
});
}
}
import { APIGatewayProxyHandlerV2 } from 'aws-lambda';
import { PutTraceSegmentsCommand, XRayClient } from '@aws-sdk/client-xray';
import * as crypto from 'crypto'
type Trace = {
segments: {
[key: string]: {
startTime: number;
endTime: number;
metrics?: {
[key: string]: number
},
}
}
}
const client = new XRayClient();
function generateId(len: number = 8) {
return crypto.randomBytes(len).toString('hex');
}
export const handler: APIGatewayProxyHandlerV2 = async (event) => {
const trace: Trace = JSON.parse(event.body ?? '{}');
const origin = event.headers['origin'] ?? 'unknown';
const traceId = `1-${Math.floor(Date.now() / 1000).toString(16)}-${generateId(12)}`;
const min = Math.min(...Object.values(trace.segments).map(v => v.startTime));
const max = Math.max(...Object.values(trace.segments).map(v => v.endTime));
const input = {
name: origin,
id: generateId(),
trace_id: traceId,
start_time: min / 1000,
end_time: max / 1000,
subsegments: Object.entries(trace.segments).map(([key, value]) => ({
id: generateId(),
name: key,
start_time: value.startTime / 1000,
end_time: value.endTime / 1000,
annotations: value.metrics,
})),
annotations: {
userAgent: event.headers['user-agent'] ?? 'unknown'
}
};
console.log(input)
const command: PutTraceSegmentsCommand = new PutTraceSegmentsCommand({
TraceSegmentDocuments: [JSON.stringify(input)]
})
const resp = await client.send(command)
console.log(resp)
return {
statusCode: 204,
}
};
@kudoh
Copy link
Author

kudoh commented May 31, 2024

{
  "$metadata": {
    "httpStatusCode": 200,
    "requestId": "e5ddb83d-0066-4a0b-ac6b-5328b5baeea4",
    "attempts": 1,
    "totalRetryDelay": 0
  },
  "ApproximateTime": "2024-05-31T01:00:00.000Z",
  "NextToken": "htu98DIpoYx+ky8XKeC/ZdjpVkN4IkVwlqrz6JUETWQyT+3FKWwVnCtdtv9RQ16X",
  "TraceSummaries": [
    {
      "Annotations": {
        "FCP": [
          {
            "AnnotationValue": {
              "NumberValue": 36
            }
          }
        ],
        "FID": [
          {
            "AnnotationValue": {
              "NumberValue": 2.5
            }
          }
        ],
        "LCP": [
          {
            "AnnotationValue": {
              "NumberValue": 36
            }
          }
        ],
        "TTFB": [
          {
            "AnnotationValue": {
              "NumberValue": 18.2
            }
          }
        ],
        "userAgent": [
          {
            "AnnotationValue": {
              "StringValue": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36"
            }
          }
        ]
      },
      "AvailabilityZones": [],
      "Duration": 8,
      "EntryPoint": {
        "Name": "http://localhost:3000",
        "Names": [
          "http://localhost:3000"
        ]
      },
      "ErrorRootCauses": [],
      "FaultRootCauses": [],
      "HasError": false,
      "HasFault": false,
      "HasThrottle": false,
      "Http": {},
      "Id": "1-66592a25-10ed50d4739c50e344a501a2",
      "InstanceIds": [],
      "IsPartial": false,
      "ResourceARNs": [],
      "ResponseTime": 8,
      "ResponseTimeRootCauses": [],
      "Revision": 1,
      "ServiceIds": [
        {
          "Name": "http://localhost:3000",
          "Names": [
            "http://localhost:3000"
          ],
          "Type": "client"
        },
        {
          "Name": "http://localhost:3000",
          "Names": [
            "http://localhost:3000"
          ]
        }
      ],
      "StartTime": "2024-05-31T01:38:35.000Z",
      "Users": []
    },
    {
      "Annotations": {
        "FCP": [
          {
            "AnnotationValue": {
              "NumberValue": 36
            }
          }
        ],
        "FID": [
          {
            "AnnotationValue": {
              "NumberValue": 2.5
            }
          }
        ],
        "LCP": [
          {
            "AnnotationValue": {
              "NumberValue": 36
            }
          }
        ],
        "TTFB": [
          {
            "AnnotationValue": {
              "NumberValue": 18.2
            }
          }
        ],
        "userAgent": [
          {
            "AnnotationValue": {
              "StringValue": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36"
            }
          }
        ]
      },
      "AvailabilityZones": [],
      "Duration": 8,
      "EntryPoint": {
        "Name": "http://localhost:3000",
        "Names": [
          "http://localhost:3000"
        ]
      },
      "ErrorRootCauses": [],
      "FaultRootCauses": [],
      "HasError": false,
      "HasFault": false,
      "HasThrottle": false,
      "Http": {},
      "Id": "1-66592a24-b8701a4543dd24deecc8404a",
      "InstanceIds": [],
      "IsPartial": false,
      "ResourceARNs": [],
      "ResponseTime": 8,
      "ResponseTimeRootCauses": [],
      "Revision": 1,
      "ServiceIds": [
        {
          "Name": "http://localhost:3000",
          "Names": [
            "http://localhost:3000"
          ],
          "Type": "client"
        },
        {
          "Name": "http://localhost:3000",
          "Names": [
            "http://localhost:3000"
          ]
        }
      ],
      "StartTime": "2024-05-31T01:38:34.000Z",
      "Users": []
    },
    {
      "Annotations": {
        "FCP": [
          {
            "AnnotationValue": {
              "NumberValue": 36
            }
          }
        ],
        "FID": [
          {
            "AnnotationValue": {
              "NumberValue": 2.5
            }
          }
        ],
        "LCP": [
          {
            "AnnotationValue": {
              "NumberValue": 36
            }
          }
        ],
        "TTFB": [
          {
            "AnnotationValue": {
              "NumberValue": 18.2
            }
          }
        ],
        "userAgent": [
          {
            "AnnotationValue": {
              "StringValue": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36"
            }
          }
        ]
      },
      "AvailabilityZones": [],
      "Duration": 8,
      "EntryPoint": {
        "Name": "http://localhost:3000",
        "Names": [
          "http://localhost:3000"
        ]
      },
      "ErrorRootCauses": [],
      "FaultRootCauses": [],
      "HasError": false,
      "HasFault": false,
      "HasThrottle": false,
      "Http": {},
      "Id": "1-66592a23-c94baf8ea8de377eac545fc9",
      "InstanceIds": [],
      "IsPartial": false,
      "ResourceARNs": [],
      "ResponseTime": 8,
      "ResponseTimeRootCauses": [],
      "Revision": 1,
      "ServiceIds": [
        {
          "Name": "http://localhost:3000",
          "Names": [
            "http://localhost:3000"
          ],
          "Type": "client"
        },
        {
          "Name": "http://localhost:3000",
          "Names": [
            "http://localhost:3000"
          ]
        }
      ],
      "StartTime": "2024-05-31T01:38:33.000Z",
      "Users": []
    }
  ],
  "TracesProcessedCount": 20
}

@kudoh
Copy link
Author

kudoh commented May 31, 2024

{
  "$metadata": {
    "httpStatusCode": 200,
    "requestId": "96ac8d92-9902-4ee9-a932-1f6d33681b51",
    "attempts": 1,
    "totalRetryDelay": 0
  },
  "Traces": [
    {
      "Duration": 8,
      "Id": "1-66592a23-c94baf8ea8de377eac545fc9",
      "LimitExceeded": false,
      "Segments": [
        {
          "Document": "{\"id\":\"c5cfe1da9b692c3d\",\"name\":\"http://localhost:3000\",\"start_time\":1.717119513474E9,\"trace_id\":\"1-66592a23-c94baf8ea8de377eac545fc9\",\"end_time\":1.717119521474E9,\"annotations\":{\"userAgent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36\"},\"subsegments\":[{\"id\":\"d5ce13de6b80f459\",\"name\":\"/complete\",\"start_time\":1.717119518474E9,\"end_time\":1.717119521474E9},{\"id\":\"8604cc723d83fbcc\",\"name\":\"/option\",\"start_time\":1.717119515474E9,\"end_time\":1.717119516474E9},{\"id\":\"0580cff5bdfa56c3\",\"name\":\"/\",\"start_time\":1.717119513474E9,\"end_time\":1.717119514474E9,\"annotations\":{\"FID\":2.5,\"FCP\":36.0,\"LCP\":36.0,\"TTFB\":18.2}},{\"id\":\"9e189f3297b153c5\",\"name\":\"/input\",\"start_time\":1.717119517474E9,\"end_time\":1.717119517974E9}]}",
          "Id": "c5cfe1da9b692c3d"
        }
      ]
    }
  ],
  "UnprocessedTraceIds": []
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment