1+ import http from 'k6/http' ;
2+ import { check , sleep } from 'k6' ;
3+ import exec from 'k6/execution' ;
4+ import encoding from 'k6/encoding' ;
5+ import { randomString , randomItem , randomIntBetween , uuidv4 } from 'https://jslib.k6.io/k6-utils/1.4.0/index.js' ;
6+
7+ // config for load test, uncomment to perform load test for an hour
8+ // export const options = {
9+ // discardResponseBodies: true,
10+ // // Key configurations for avg load test in this section
11+ // stages: [
12+ // { duration: '5m', target: 10 },
13+ // { duration: '60m', target: 8 },
14+ // { duration: '5m', target: 0 },
15+ // ],
16+ // };
17+
18+ // default options for all tests
19+ export const options = {
20+ discardResponseBodies : true ,
21+ scenarios : {
22+ contacts : {
23+ executor : 'constant-vus' ,
24+ vus : 10 ,
25+ duration : "5m" ,
26+ } ,
27+ } ,
28+ } ;
29+
30+ // Helper function to generate current ISO time
31+ function currentIsoTime ( ) {
32+ let event = new Date ( ) ;
33+ return event . toISOString ( ) ;
34+ }
35+
36+ // Helper function to generate unix nano time
37+ function currentUnixNanoTime ( ) {
38+ return new Date ( ) . toISOString ( ) ;
39+ }
40+
41+ // Helper to generate trace ID (hex string)
42+ function generateTraceId ( ) {
43+ return [ ...Array ( 32 ) ] . map ( ( ) => Math . floor ( Math . random ( ) * 16 ) . toString ( 16 ) ) . join ( '' ) ;
44+ }
45+
46+ // Helper to generate span ID (hex string)
47+ function generateSpanId ( ) {
48+ return [ ...Array ( 16 ) ] . map ( ( ) => Math . floor ( Math . random ( ) * 16 ) . toString ( 16 ) ) . join ( '' ) ;
49+ }
50+
51+ // Generate HTTP methods
52+ function randomHttpMethod ( ) {
53+ return randomItem ( [ "GET" , "POST" , "PUT" , "DELETE" , "PATCH" ] ) ;
54+ }
55+
56+ // Generate API endpoints
57+ function randomApiEndpoint ( ) {
58+ const endpoints = [
59+ "/api/products/{id}" ,
60+ "/api/cart" ,
61+ "/api/checkout" ,
62+ "/api/users/{id}" ,
63+ "/api/orders/{id}" ,
64+ "/api/recommendations" ,
65+ "/api/categories" ,
66+ "/api/search" ,
67+ "/api/auth/login" ,
68+ "/api/auth/logout"
69+ ] ;
70+
71+ let endpoint = randomItem ( endpoints ) ;
72+
73+ // Replace {id} with random product/user ID if present
74+ if ( endpoint . includes ( "{id}" ) ) {
75+ const id = randomItem ( [
76+ "0PUK6V6EV0" , "1YMWWN1N4O" , "2ZYFJ3GM2N" ,
77+ "66VCHSJNUP" , "6E92ZMYYFZ" , "9SIQT8TOJO" ,
78+ "L9ECAV7KIM" , "LS4PSXUNUM" , "OLJCESPC7Z"
79+ ] ) ;
80+ endpoint = endpoint . replace ( "{id}" , id ) ;
81+ }
82+
83+ return endpoint ;
84+ }
85+
86+ // Generate IP addresses
87+ function randomIpAddress ( ) {
88+ return `172.18.0.${ randomIntBetween ( 1 , 40 ) } ` ;
89+ }
90+
91+ // Generate port number
92+ function randomPort ( ) {
93+ return randomIntBetween ( 8000 , 60000 ) ;
94+ }
95+
96+ function randomStatusCode ( ) {
97+ return randomItem ( [ 200 , 206 , 304 , 400 , 404 , 500 , 503 ] ) ;
98+ }
99+
100+ // Map status code to appropriate severity number and text
101+ function getSeverityFromStatusCode ( statusCode ) {
102+ // Define severity mapping based on status code ranges
103+ if ( statusCode >= 100 && statusCode < 200 ) {
104+ // 1xx - Informational
105+ return { number : randomItem ( [ 9 , 10 , 11 , 12 ] ) , text : "INFO" } ;
106+ } else if ( statusCode >= 200 && statusCode < 300 ) {
107+ // 2xx - Success
108+ return { number : randomItem ( [ 9 , 10 , 11 , 12 ] ) , text : "INFO" } ;
109+ } else if ( statusCode >= 300 && statusCode < 400 ) {
110+ // 3xx - Redirection
111+ return { number : randomItem ( [ 9 , 10 , 11 , 12 ] ) , text : "INFO" } ;
112+ } else if ( statusCode >= 400 && statusCode < 500 ) {
113+ // 4xx - Client Error
114+ return { number : randomItem ( [ 13 , 14 , 15 , 16 ] ) , text : "WARN" } ;
115+ } else if ( statusCode >= 500 ) {
116+ // 5xx - Server Error
117+ const severityRoll = Math . random ( ) ;
118+ if ( severityRoll < 0.7 ) {
119+ return { number : randomItem ( [ 17 , 18 , 19 , 20 ] ) , text : "ERROR" } ;
120+ } else {
121+ return { number : randomItem ( [ 21 , 22 , 23 , 24 ] ) , text : "FATAL" } ;
122+ }
123+ } else {
124+ // Default or unknown
125+ return { number : 0 , text : "SEVERITY_NUMBER_UNSPECIFIED" } ;
126+ }
127+ }
128+
129+ // Generate random user agent
130+ function randomUserAgent ( ) {
131+ const userAgents = [
132+ "python-requests/2.32.3" ,
133+ "curl/7.88.1" ,
134+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.54 Safari/537.36" ,
135+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Safari/605.1.15" ,
136+ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.75 Safari/537.36" ,
137+ "Mozilla/5.0 (iPhone; CPU iPhone OS 15_4_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148" ,
138+ "PostmanRuntime/7.29.0"
139+ ] ;
140+ return randomItem ( userAgents ) ;
141+ }
142+
143+ // Generate service names
144+ function randomServiceName ( ) {
145+ return randomItem ( [
146+ "frontend-proxy" ,
147+ "product-service" ,
148+ "cart-service" ,
149+ "user-service" ,
150+ "checkout-service" ,
151+ "recommendation-service" ,
152+ "auth-service"
153+ ] ) ;
154+ }
155+
156+ // Generate body content for log
157+ function generateLogBody ( method , path , statusCode , userAgent ) {
158+ const timestamp = currentIsoTime ( ) ;
159+ const bytesSent = randomIntBetween ( 100 , 2000 ) ;
160+ const duration = randomIntBetween ( 1 , 100 ) ;
161+ const durationMs = randomIntBetween ( 1 , 20 ) ;
162+ const requestId = uuidv4 ( ) . replace ( / - / g, '-' ) ;
163+ const upstreamAddress = `${ randomIpAddress ( ) } :8080` ;
164+ const sourceAddress = randomIpAddress ( ) ;
165+ const destAddress = randomIpAddress ( ) ;
166+ const sourcePort = randomPort ( ) ;
167+ const destPort = 8080 ;
168+ const clientPort = randomPort ( ) ;
169+
170+ return `[${ timestamp } ] "${ method } ${ path } HTTP/1.1" ${ statusCode } - via_upstream - "-" 0 ${ bytesSent } ${ duration } ${ durationMs } "-" "${ userAgent } " "${ requestId } " "frontend-proxy:8080" "${ upstreamAddress } " frontend ${ sourceAddress } :${ sourcePort } ${ destAddress } :${ destPort } ${ sourceAddress } :${ clientPort } - -\n` ;
171+ }
172+
173+ // Generate a single OTEL log entry
174+ function generateOtelLog ( ) {
175+ const method = randomHttpMethod ( ) ;
176+ const path = randomApiEndpoint ( ) ;
177+ const statusCode = randomStatusCode ( ) ;
178+ const userAgent = randomUserAgent ( ) ;
179+ const serviceName = randomServiceName ( ) ;
180+ const traceId = randomId ( 32 ) ;
181+ const spanId = randomId ( 16 ) ;
182+ const timeUnixNano = currentUnixNanoTime ( ) ;
183+ const destAddress = randomIpAddress ( ) ;
184+ const serverAddress = randomIpAddress ( ) ;
185+ const sourceAddress = randomIpAddress ( ) ;
186+ const upstreamHost = randomIpAddress ( ) ;
187+
188+ // Get severity based on status code
189+ const severity = getSeverityFromStatusCode ( statusCode ) ;
190+
191+ return {
192+ "body" : generateLogBody ( method , path , statusCode , userAgent ) ,
193+ "observed_time_unix_nano" : timeUnixNano , // Using the same value as time_unix_nano
194+ "destination.address" : destAddress ,
195+ "event.name" : "proxy.access" ,
196+ "server.address" : serverAddress ,
197+ "source.address" : sourceAddress ,
198+ "upstream.cluster" : randomItem ( [ "frontend" , "backend" , "auth" , "product" ] ) ,
199+ "upstream.host" : upstreamHost ,
200+ "user_agent.original" : randomUserAgent ( ) ,
201+ "service.name" : serviceName ,
202+ "severity_number" : severity . number ,
203+ "severity_text" : severity . text ,
204+ "span_id" : spanId ,
205+ "time_unix_nano" : timeUnixNano ,
206+ "trace_id" : traceId ,
207+ "url.full" : `http://${ serviceName } :8080${ path } ` ,
208+ "url.path" : path ,
209+ } ;
210+ }
211+
212+ // Generate a batch of OTEL logs
213+ function generateOtelLogs ( count ) {
214+ const logs = [ ] ;
215+ for ( let i = 0 ; i < count ; i ++ ) {
216+ logs . push ( generateOtelLog ( ) ) ;
217+ }
218+ return logs ;
219+ }
220+
221+ // Get events per call from environment variable
222+ function eventsPerCall ( ) {
223+ return Number ( __ENV . P_EVENTS_COUNT ) || 100 ;
224+ }
225+
226+ export default function ( ) {
227+ const url = `${ __ENV . P_URL } /api/v1/ingest` ;
228+ const credentials = `${ __ENV . P_USERNAME } :${ __ENV . P_PASSWORD } ` ;
229+ const encodedCredentials = encoding . b64encode ( credentials ) ;
230+
231+ const params = {
232+ headers : {
233+ 'Content-Type' : 'application/json' ,
234+ 'Authorization' : 'Basic ' + `${ encodedCredentials } ` ,
235+ 'X-P-STREAM' : `${ __ENV . P_STREAM } ` ,
236+ 'X-P-META-Host' : '10.116.0.3' ,
237+ 'X-P-META-Source' : 'otel-logs-generator'
238+ }
239+ } ;
240+
241+ let events = eventsPerCall ( ) ;
242+
243+ let batchRequests = JSON . stringify ( generateOtelLogs ( events ) ) ;
244+
245+ let response = http . post ( url , batchRequests , params ) ;
246+ let date = new Date ( ) ;
247+
248+ if ( ! check ( response , {
249+ 'status code MUST be 200' : ( res ) => res . status == 200 ,
250+ } ) ) {
251+ console . log ( `Time: ${ date } , Response: ${ response . status } ` ) ;
252+ }
253+ }
0 commit comments