@@ -79,7 +79,6 @@ function decodeCursor(cursor: string): LogCursor | null {
7979
8080// Convert ClickHouse kind to display level
8181function kindToLevel ( kind : string , status : string ) : LogLevel {
82- // CANCELLED status takes precedence
8382 if ( status === "CANCELLED" ) {
8483 return "CANCELLED" ;
8584 }
@@ -127,7 +126,7 @@ function levelToKindsAndStatuses(
127126 }
128127}
129128
130- // Convert nanoseconds to milliseconds
129+
131130function convertDateToNanoseconds ( date : Date ) : bigint {
132131 return BigInt ( date . getTime ( ) ) * 1_000_000n ;
133132}
@@ -161,21 +160,17 @@ export class LogsListPresenter {
161160 search,
162161 from,
163162 to,
164- direction = "forward" ,
165163 cursor,
166164 pageSize = DEFAULT_PAGE_SIZE ,
167165 includeDebugLogs = true ,
168166 } : LogsListOptions
169167 ) {
170- // Get time values from raw values (including default period)
171168 const time = timeFilters ( {
172169 period,
173170 from,
174171 to,
175172 } ) ;
176173
177- // If period is provided but from/to are not calculated, convert period to date range
178- // This is needed because timeFilters doesn't convert period to actual dates
179174 let effectiveFrom = time . from ;
180175 let effectiveTo = time . to ;
181176
@@ -208,13 +203,11 @@ export class LogsListPresenter {
208203 ( search !== undefined && search !== "" ) ||
209204 ! time . isDefault ;
210205
211- // Get all possible tasks
212206 const possibleTasksAsync = getAllTaskIdentifiers (
213207 this . replica ,
214208 environmentId
215209 ) ;
216210
217- // Get possible bulk actions
218211 const bulkActionsAsync = this . replica . bulkActionGroup . findMany ( {
219212 select : {
220213 friendlyId : true ,
@@ -239,7 +232,6 @@ export class LogsListPresenter {
239232 findDisplayableEnvironment ( environmentId , userId ) ,
240233 ] ) ;
241234
242- // If the bulk action isn't in the most recent ones, add it separately
243235 if (
244236 bulkId &&
245237 ! bulkActions . some ( ( bulkAction ) => bulkAction . friendlyId === bulkId )
@@ -306,7 +298,6 @@ export class LogsListPresenter {
306298 } ,
307299 } ) ;
308300
309- // If no matching runs, return empty result
310301 if ( runIds . length === 0 ) {
311302 return {
312303 logs : [ ] ,
@@ -341,15 +332,12 @@ export class LogsListPresenter {
341332 }
342333 }
343334
344- // Build ClickHouse query
345335 const queryBuilder = this . clickhouse . taskEventsV2 . logsListQueryBuilder ( ) ;
346336
347- // PREWHERE for primary key columns (first in ORDER BY, uses index efficiently)
348337 queryBuilder . prewhere ( "environment_id = {environmentId: String}" , {
349338 environmentId,
350339 } ) ;
351340
352- // WHERE for non-indexed columns
353341 queryBuilder . where ( "organization_id = {organizationId: String}" , {
354342 organizationId,
355343 } ) ;
@@ -358,7 +346,6 @@ export class LogsListPresenter {
358346 // Time filters - inserted_at in PREWHERE for partition pruning, start_time in WHERE
359347 if ( effectiveFrom ) {
360348 const fromNs = convertDateToNanoseconds ( effectiveFrom ) . toString ( ) ;
361- // PREWHERE for partition key (inserted_at) - dramatically reduces data scanned
362349 queryBuilder . prewhere ( "inserted_at >= {insertedAtStart: DateTime64(3)}" , {
363350 insertedAtStart : convertDateToClickhouseDateTime ( effectiveFrom ) ,
364351 } ) ;
@@ -370,7 +357,6 @@ export class LogsListPresenter {
370357 if ( effectiveTo ) {
371358 const clampedTo = effectiveTo > new Date ( ) ? new Date ( ) : effectiveTo ;
372359 const toNs = convertDateToNanoseconds ( clampedTo ) . toString ( ) ;
373- // PREWHERE for partition key (inserted_at) - dramatically reduces data scanned
374360 queryBuilder . prewhere ( "inserted_at <= {insertedAtEnd: DateTime64(3)}" , {
375361 insertedAtEnd : convertDateToClickhouseDateTime ( clampedTo ) ,
376362 } ) ;
@@ -391,23 +377,22 @@ export class LogsListPresenter {
391377 queryBuilder . where ( "run_id IN {runIds: Array(String)}" , { runIds } ) ;
392378 }
393379
394- // Case-insensitive search in message and status fields
380+ // Case-insensitive search in message, attributes, and status fields
395381 if ( search && search . trim ( ) !== "" ) {
396382 const searchTerm = search . trim ( ) ;
397383 queryBuilder . where (
398- "(message ilike {searchPattern: String} OR status = {statusTerm: String})" ,
384+ "(message ilike {searchPattern: String} OR attributes_text ilike {searchPattern: String} OR status = {statusTerm: String})" ,
399385 {
400386 searchPattern : `%${ searchTerm } %` ,
401387 statusTerm : searchTerm . toUpperCase ( ) ,
402388 }
403389 ) ;
404390 }
405391
406- // Level filter (map display levels to ClickHouse kinds/statuses)
392+
407393 if ( levels && levels . length > 0 ) {
408394 const conditions : string [ ] = [ ] ;
409- const params : Record < string , unknown > = { } ;
410- const hasTraceLevel = levels . includes ( "TRACE" ) ;
395+ const params : Record < string , any > = { } ;
411396 const hasErrorOrCancelledLevel = levels . includes ( "ERROR" ) || levels . includes ( "CANCELLED" ) ;
412397
413398 for ( const level of levels ) {
@@ -440,23 +425,17 @@ export class LogsListPresenter {
440425 }
441426
442427 if ( conditions . length > 0 ) {
443- queryBuilder . where ( `(${ conditions . join ( " OR " ) } )` ) ;
428+ queryBuilder . where ( `(${ conditions . join ( " OR " ) } )` , params as any ) ;
444429 }
445430 }
446431
447- // Exclude DEBUG logs if not included
432+ // Debug logs are available only to admins
448433 if ( includeDebugLogs === false ) {
449434 queryBuilder . where ( "kind NOT IN {debugKinds: Array(String)}" , {
450435 debugKinds : [ "DEBUG_EVENT" , "LOG_DEBUG" ] ,
451436 } ) ;
452437 }
453438
454- // Exclude TRACE logs with PARTIAL status
455- // const traceKinds = ["SPAN", "ANCESTOR_OVERRIDE", "SPAN_EVENT"];
456- // queryBuilder.where(
457- // "NOT (kind IN {traceKinds: Array(String)} AND status = 'PARTIAL')",
458- // { traceKinds }
459- // );
460439 queryBuilder . where ( "NOT (kind = 'SPAN' AND status = 'PARTIAL')" ) ;
461440
462441
@@ -474,13 +453,11 @@ export class LogsListPresenter {
474453 ) ;
475454 }
476455
477- // Order by newest first
478456 queryBuilder . orderBy ( "start_time DESC, trace_id DESC, span_id DESC, run_id DESC" ) ;
479457
480458 // Limit + 1 to check if there are more results
481459 queryBuilder . limit ( pageSize + 1 ) ;
482460
483- // Execute query
484461 const [ queryError , records ] = await queryBuilder . execute ( ) ;
485462
486463 if ( queryError ) {
@@ -505,20 +482,37 @@ export class LogsListPresenter {
505482
506483 // Transform results
507484 // Use :: as separator since dash conflicts with date format in start_time
508- const transformedLogs = logs . map ( ( log ) => ( {
509- id : `${ log . trace_id } ::${ log . span_id } ::${ log . run_id } ::${ log . start_time } ` ,
510- runId : log . run_id ,
511- taskIdentifier : log . task_identifier ,
512- startTime : convertClickhouseDateTime64ToJsDate ( log . start_time ) . toISOString ( ) ,
513- traceId : log . trace_id ,
514- spanId : log . span_id ,
515- parentSpanId : log . parent_span_id || null ,
516- message : log . message ,
517- kind : log . kind ,
518- status : log . status ,
519- duration : typeof log . duration === "number" ? log . duration : Number ( log . duration ) ,
520- level : kindToLevel ( log . kind , log . status ) ,
521- } ) ) ;
485+ const transformedLogs = logs . map ( ( log ) => {
486+ let displayMessage = log . message ;
487+
488+ // For error logs with status ERROR, try to extract error message from attributes
489+ if ( log . status === "ERROR" && log . attributes ) {
490+ try {
491+ let attributes = log . attributes as Record < string , any > ;
492+
493+ if ( attributes ?. error ?. message && typeof attributes . error . message === 'string' ) {
494+ displayMessage = attributes . error . message ;
495+ }
496+ } catch {
497+ // If attributes parsing fails, use the regular message
498+ }
499+ }
500+
501+ return {
502+ id : `${ log . trace_id } ::${ log . span_id } ::${ log . run_id } ::${ log . start_time } ` ,
503+ runId : log . run_id ,
504+ taskIdentifier : log . task_identifier ,
505+ startTime : convertClickhouseDateTime64ToJsDate ( log . start_time ) . toISOString ( ) ,
506+ traceId : log . trace_id ,
507+ spanId : log . span_id ,
508+ parentSpanId : log . parent_span_id || null ,
509+ message : displayMessage ,
510+ kind : log . kind ,
511+ status : log . status ,
512+ duration : typeof log . duration === "number" ? log . duration : Number ( log . duration ) ,
513+ level : kindToLevel ( log . kind , log . status ) ,
514+ } ;
515+ } ) ;
522516
523517 return {
524518 logs : transformedLogs ,
0 commit comments