-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
6e97e16
commit 3cf6233
Showing
18 changed files
with
357 additions
and
285 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
[<EntryPoint>] | ||
let main _ = 0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>net9.0</TargetFramework> | ||
<GenerateDocumentationFile>true</GenerateDocumentationFile> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<Compile Include="Log.fs" /> | ||
<Compile Include="Filter.fs" /> | ||
<Compile Include="Parser.fs" /> | ||
<Compile Include="Grok.fs" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="FParsec" Version="1.1.1" /> | ||
<PackageReference Include="grok.net" Version="2.0.0" /> | ||
<PackageReference Include="PCRE.NET" Version="1.1.0" /> | ||
</ItemGroup> | ||
|
||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
namespace Analog.Core | ||
|
||
type Operator = | ||
| EqualOperator | ||
| NotEqualOperator | ||
| GreaterThanOperator | ||
| GreaterThanOrEqualOperator | ||
| LessThanOperator | ||
| LessThanOrEqualOperator | ||
| AndOperator | ||
| OrOperator | ||
|
||
type Expression = | ||
| LiteralExpression of Literal | ||
| MemberExpression of string | ||
| BinaryExpression of Expression * Operator * Expression | ||
|
||
[<RequireQualifiedAccess>] | ||
module Filter = | ||
type private Evaluation = | ||
| TemporaryEvaluation of Literal option | ||
| FinalEvaluation of bool | ||
|
||
let eval expression entry = | ||
let compareLiteral (left: Literal option) (right: Literal option) comparer = | ||
match left, right with | ||
| Some left, Some right -> | ||
match left, right with | ||
| StringLiteral _, StringLiteral _ -> comparer left right | ||
| NumberLiteral _, NumberLiteral _ -> comparer left right | ||
| BooleanLiteral _, BooleanLiteral _ -> comparer left right | ||
| TimestampLiteral _, TimestampLiteral _ -> comparer left right | ||
| _ -> false | ||
| _ -> false | ||
|
||
let combineLiteral (left: Literal option) (right: Literal option) combiner = | ||
match left, right with | ||
| Some left, Some right -> | ||
match left, right with | ||
| BooleanLiteral left, BooleanLiteral right -> combiner left right | ||
| _ -> false | ||
| _ -> false | ||
|
||
let wrapFinal = BooleanLiteral >> Some | ||
|
||
let compareEvaluation (left: Evaluation) (right: Evaluation) comparer = | ||
match left, right with | ||
| TemporaryEvaluation left, TemporaryEvaluation right -> compareLiteral left right comparer | ||
| TemporaryEvaluation left, FinalEvaluation right -> compareLiteral left (wrapFinal right) comparer | ||
| FinalEvaluation left, TemporaryEvaluation right -> compareLiteral (wrapFinal left) right comparer | ||
| FinalEvaluation left, FinalEvaluation right -> compareLiteral (wrapFinal left) (wrapFinal right) comparer | ||
|
||
let combineEvaluation (left: Evaluation) (right: Evaluation) combiner = | ||
match left, right with | ||
| TemporaryEvaluation left, TemporaryEvaluation right -> combineLiteral left right combiner | ||
| TemporaryEvaluation left, FinalEvaluation right -> combineLiteral left (wrapFinal right) combiner | ||
| FinalEvaluation left, TemporaryEvaluation right -> combineLiteral (wrapFinal left) right combiner | ||
| FinalEvaluation left, FinalEvaluation right -> combineLiteral (wrapFinal left) (wrapFinal right) combiner | ||
|
||
let evaluateOperator (left: Evaluation) (operator: Operator) (right: Evaluation) = | ||
match operator with | ||
| EqualOperator -> compareEvaluation left right (=) | ||
| NotEqualOperator -> compareEvaluation left right (<>) | ||
| GreaterThanOperator -> compareEvaluation left right (>) | ||
| GreaterThanOrEqualOperator -> compareEvaluation left right (>=) | ||
| LessThanOperator -> compareEvaluation left right (<) | ||
| LessThanOrEqualOperator -> compareEvaluation left right (<=) | ||
| AndOperator -> combineEvaluation left right (&&) | ||
| OrOperator -> combineEvaluation left right (||) | ||
|
||
let rec loop (expression: Expression) (entry: Log) : Evaluation = | ||
match expression with | ||
| LiteralExpression right -> right |> Option.Some |> TemporaryEvaluation | ||
| MemberExpression field -> entry |> Log.get field |> TemporaryEvaluation | ||
| BinaryExpression(left, operator, right) -> | ||
let left = loop left entry | ||
let right = loop right entry | ||
evaluateOperator left operator right |> FinalEvaluation | ||
|
||
match loop expression entry with | ||
| TemporaryEvaluation temp -> temp.IsSome | ||
| FinalEvaluation final -> final | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
namespace Analog.Core | ||
|
||
type GrokGroup = GrokGroup of Map<string, string> | ||
|
||
[<RequireQualifiedAccess>] | ||
module Grok = | ||
open GrokNet | ||
|
||
let create (pattern: string) : Result<Grok, string> = | ||
try | ||
Grok(pattern) |> Result.Ok | ||
with err -> | ||
$"Grok initialization failed with error: {err.Message}" |> Result.Error | ||
|
||
let extract (text: string) (pattern: Grok) : Result<GrokResult, string> = | ||
try | ||
pattern.Parse text |> Result.Ok | ||
with err -> | ||
$"Grok extraction failed with error: {err.Message}" |> Result.Error | ||
|
||
let pattern: Grok = | ||
Grok("\[%{TIMESTAMP_ISO8601:timestamp}\] \[%{LOGLEVEL:loglevel}\] %{GREEDYDATA:message}") | ||
|
||
let group: GrokResult -> GrokGroup list = | ||
Seq.fold | ||
(fun list item -> | ||
match list with | ||
| [] -> [ Map([ item.Key, string item.Value ]) ] | ||
| head :: tail -> | ||
if head |> Map.containsKey item.Key then | ||
[ Map([ item.Key, string item.Value ]); head ] @ tail | ||
else | ||
(head |> Map.add item.Key (string item.Value)) :: tail) | ||
List.empty | ||
>> List.rev | ||
>> List.map GrokGroup | ||
|
||
let transform: GrokGroup list -> Log list = | ||
List.map (fun (GrokGroup group) -> | ||
group | ||
|> Map.toSeq | ||
|> Seq.choose (fun (key, value) -> | ||
match Parser.literal |> Parser.parse value with | ||
| Ok value -> Some(key, value) | ||
| Error _ -> None) | ||
|> Map.ofSeq | ||
|> Log) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
namespace Analog.Core | ||
|
||
open System | ||
|
||
type Literal = | ||
| StringLiteral of string | ||
| NumberLiteral of float | ||
| BooleanLiteral of bool | ||
| TimestampLiteral of DateTimeOffset | ||
|
||
type Log = Log of Map<string, Literal> | ||
|
||
[<RequireQualifiedAccess>] | ||
module Log = | ||
let get key (Log entry) = entry |> Map.tryFind key | ||
let empty = Map.empty |> Log |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
[<RequireQualifiedAccess>] | ||
module Analog.Core.Parser | ||
|
||
open System | ||
open FParsec | ||
|
||
let parse input parser = | ||
run parser input | ||
|> function | ||
| Success(value, _, _) -> Result.Ok value | ||
| Failure(error, _, _) -> Result.Error error | ||
|
||
let dateTimeOffset: Parser<_, unit> = | ||
restOfLine false | ||
>>= fun input -> | ||
try | ||
DateTimeOffset.Parse input |> preturn | ||
with err -> | ||
fail err.Message | ||
|> attempt | ||
|
||
let floatFinite: Parser<_, unit> = | ||
pfloat | ||
>>= fun res -> | ||
if Double.IsInfinity res || Double.IsNaN res then | ||
fail "Number cannot be infinite or NaN" | ||
else | ||
preturn res | ||
|> attempt | ||
|
||
let boolCI: Parser<_, unit> = | ||
choice [ pstringCI "true" >>% true; pstringCI "false" >>% false ] | ||
|
||
let stringQuoted: Parser<string, unit> = | ||
skipChar '\'' >>. manyCharsTill anyChar (skipChar '\'') | ||
|
||
let literal: Parser<_, unit> = | ||
choice | ||
[ dateTimeOffset |>> TimestampLiteral | ||
floatFinite |>> Literal.NumberLiteral | ||
boolCI |>> BooleanLiteral | ||
restOfLine true |>> StringLiteral ] | ||
|
||
let expression: Parser<_, unit> = | ||
let literalExpression: Parser<_, unit> = | ||
choice | ||
[ dateTimeOffset |>> TimestampLiteral .>> spaces | ||
floatFinite |>> Literal.NumberLiteral .>> spaces | ||
boolCI |>> BooleanLiteral .>> spaces | ||
stringQuoted |>> StringLiteral .>> spaces ] | ||
|>> LiteralExpression | ||
|
||
let memberExpression: Parser<_, unit> = | ||
many1Chars (letter <|> digit) |>> MemberExpression .>> spaces | ||
|
||
let builder = OperatorPrecedenceParser<Expression, _, _>() | ||
builder.TermParser <- choice [ literalExpression; memberExpression ] | ||
let bin op left right = BinaryExpression(left, op, right) | ||
builder.AddOperator(InfixOperator("&", spaces, 1, Associativity.Left, bin AndOperator)) | ||
builder.AddOperator(InfixOperator("|", spaces, 2, Associativity.Left, bin OrOperator)) | ||
builder.AddOperator(InfixOperator(">", spaces, 3, Associativity.None, bin GreaterThanOperator)) | ||
builder.AddOperator(InfixOperator(">=", spaces, 4, Associativity.None, bin GreaterThanOrEqualOperator)) | ||
builder.AddOperator(InfixOperator("<", spaces, 5, Associativity.None, bin LessThanOperator)) | ||
builder.AddOperator(InfixOperator("<=", spaces, 6, Associativity.None, bin LessThanOrEqualOperator)) | ||
builder.AddOperator(InfixOperator("=", spaces, 7, Associativity.None, bin EqualOperator)) | ||
builder.AddOperator(InfixOperator("<>", spaces, 8, Associativity.None, bin NotEqualOperator)) | ||
builder.ExpressionParser |
Oops, something went wrong.