< Summary

Information
Class: Elsa.Diagnostics.StructuredLogs.Persistence.Relational.Services.RelationalStructuredLogSqlBuilder
Assembly: Elsa.Diagnostics.StructuredLogs.Persistence.Relational
File(s): /home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Diagnostics.StructuredLogs.Persistence.Relational/Services/RelationalStructuredLogSqlBuilder.cs
Line coverage
100%
Covered lines: 83
Uncovered lines: 0
Coverable lines: 83
Total lines: 138
Line coverage: 100%
Branch coverage
100%
Covered branches: 20
Total branches: 20
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
BuildInsert()100%11100%
BuildQuery(...)100%22100%
BuildListSources()100%11100%
BuildDeleteOlderThan(...)100%11100%
BuildDeleteRowsBeyondMax(...)100%11100%
BuildFilterPredicates(...)100%1414100%
AddStringPredicate(...)100%22100%
AddPredicate(...)100%22100%
.cctor()100%11100%

File(s)

/home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Diagnostics.StructuredLogs.Persistence.Relational/Services/RelationalStructuredLogSqlBuilder.cs

#LineLine coverage
 1using Elsa.Diagnostics.StructuredLogs.Models;
 2using Elsa.Diagnostics.StructuredLogs.Options;
 3using Elsa.Diagnostics.StructuredLogs.Persistence.Relational.Contracts;
 4using Microsoft.Extensions.Options;
 5
 6namespace Elsa.Diagnostics.StructuredLogs.Persistence.Relational.Services;
 7
 258public class RelationalStructuredLogSqlBuilder(IRelationalStructuredLogDialect dialect, IOptions<StructuredLogsOptions> 
 9{
 2510    private readonly string _table = dialect.QuoteIdentifier("StructuredLogEvents");
 11
 12    public string BuildInsert()
 13    {
 114        var columns = Columns;
 115        var columnList = string.Join(", ", columns.Select(dialect.QuoteIdentifier));
 2116        var parameterList = string.Join(", ", columns.Select(x => $"{dialect.ParameterPrefix}{x}"));
 117        return $"INSERT INTO {_table} ({columnList}) VALUES ({parameterList})";
 18    }
 19
 20    public QueryDefinition BuildQuery(StructuredLogFilter filter)
 21    {
 1422        var parameters = new Dictionary<string, object?>();
 1423        var predicates = BuildFilterPredicates(filter, parameters);
 1424        var where = predicates.Count == 0 ? "" : $" WHERE {string.Join(" AND ", predicates)}";
 1425        var limit = options.Value.ClampRecentLogQueryTake(filter.Take);
 1426        var sql = $"SELECT {string.Join(", ", Columns.Select(dialect.QuoteIdentifier))} FROM {_table}{where} ORDER BY {d
 1427        sql = dialect.ApplyLimit(sql, limit);
 1428        return new(sql, parameters);
 29    }
 30
 31    public string BuildListSources()
 32    {
 133        var sourceId = dialect.QuoteIdentifier("SourceId");
 134        var receivedAt = dialect.QuoteIdentifier("ReceivedAt");
 135        return $"SELECT {sourceId}, MAX({receivedAt}) AS {dialect.QuoteIdentifier("LastSeen")} FROM {_table} GROUP BY {s
 36    }
 37
 38    public QueryDefinition BuildDeleteOlderThan(string cutoff)
 39    {
 140        return new($"DELETE FROM {_table} WHERE {dialect.QuoteIdentifier("ReceivedAt")} < {dialect.ParameterPrefix}Cutof
 41    }
 42
 43    public QueryDefinition BuildDeleteRowsBeyondMax(int maxRows)
 44    {
 145        var id = dialect.QuoteIdentifier("Id");
 146        var receivedAt = dialect.QuoteIdentifier("ReceivedAt");
 147        var sequence = dialect.QuoteIdentifier("Sequence");
 148        var selectSql = $"SELECT {id} FROM {_table} ORDER BY {receivedAt} DESC, {sequence} DESC, {id} DESC";
 149        var sql = $"DELETE FROM {_table} WHERE {id} IN ({dialect.ApplyOffset(selectSql, maxRows)})";
 150        return new(sql, new Dictionary<string, object?>());
 51    }
 52
 53    private List<string> BuildFilterPredicates(StructuredLogFilter filter, IDictionary<string, object?> parameters)
 54    {
 1455        var predicates = new List<string>();
 56
 1457        if (filter.MinimumLevel is { } minimumLevel)
 158            AddPredicate(predicates, parameters, "Level", ">=", (int)minimumLevel);
 59
 1460        if (filter.Levels is { Count: > 0 })
 61        {
 162            var names = filter.Levels.Select((level, index) =>
 163            {
 264                var name = $"Level{index}";
 265                parameters[name] = (int)level;
 266                return $"{dialect.ParameterPrefix}{name}";
 167            });
 168            predicates.Add($"{dialect.QuoteIdentifier("Level")} IN ({string.Join(", ", names)})");
 69        }
 70
 1471        if (!string.IsNullOrWhiteSpace(filter.CategoryPrefix))
 172            AddPredicate(predicates, parameters, "Category", "LIKE", $"{filter.CategoryPrefix}%");
 73
 1474        if (!string.IsNullOrWhiteSpace(filter.Text))
 75        {
 276            parameters["Text"] = $"%{filter.Text}%";
 277            var textParameter = $"{dialect.ParameterPrefix}Text";
 278            predicates.Add($"({dialect.QuoteIdentifier("Message")} LIKE {textParameter} OR {dialect.QuoteIdentifier("Mes
 79        }
 80
 1481        AddStringPredicate(predicates, parameters, "TenantId", filter.TenantId);
 1482        AddStringPredicate(predicates, parameters, "WorkflowDefinitionId", filter.WorkflowDefinitionId);
 1483        AddStringPredicate(predicates, parameters, "WorkflowInstanceId", filter.WorkflowInstanceId);
 1484        AddStringPredicate(predicates, parameters, "TraceId", filter.TraceId);
 1485        AddStringPredicate(predicates, parameters, "SpanId", filter.SpanId);
 1486        AddStringPredicate(predicates, parameters, "CorrelationId", filter.CorrelationId);
 1487        AddStringPredicate(predicates, parameters, "SourceId", filter.SourceId);
 88
 1489        if (filter.From is { } from)
 190            AddPredicate(predicates, parameters, "Timestamp", ">=", RelationalStructuredLogMapper.FormatTimestamp(from),
 91
 1492        if (filter.To is { } to)
 193            AddPredicate(predicates, parameters, "Timestamp", "<=", RelationalStructuredLogMapper.FormatTimestamp(to), "
 94
 1495        return predicates;
 96    }
 97
 98    private void AddStringPredicate(ICollection<string> predicates, IDictionary<string, object?> parameters, string colu
 99    {
 98100        if (string.IsNullOrWhiteSpace(value))
 89101            return;
 102
 9103        AddPredicate(predicates, parameters, column, "=", value);
 9104    }
 105
 106    private void AddPredicate(ICollection<string> predicates, IDictionary<string, object?> parameters, string column, st
 107    {
 13108        var name = parameterName ?? column;
 13109        predicates.Add($"{dialect.QuoteIdentifier(column)} {op} {dialect.ParameterPrefix}{name}");
 13110        parameters[name] = value;
 13111    }
 112
 1113    private static readonly string[] Columns =
 1114    [
 1115        "Id",
 1116        "Sequence",
 1117        "Timestamp",
 1118        "ReceivedAt",
 1119        "Level",
 1120        "Category",
 1121        "EventId",
 1122        "EventName",
 1123        "Message",
 1124        "MessageTemplate",
 1125        "ExceptionJson",
 1126        "ScopesJson",
 1127        "PropertiesJson",
 1128        "TraceId",
 1129        "SpanId",
 1130        "CorrelationId",
 1131        "TenantId",
 1132        "WorkflowDefinitionId",
 1133        "WorkflowInstanceId",
 1134        "SourceId"
 1135    ];
 136}
 137
 138public record QueryDefinition(string Sql, IReadOnlyDictionary<string, object?> Parameters);