version: 2
title: Quine Log Reader
contributor: https://github.com/maglietti
summary: "Ingest Quine Log Lines"
description: |-
  "This recipe processes Quine log lines using a regular expression.
  or pass `-Dthatdot.loglevel=DEBUG` to java when at runtime."

ingestStreams:
  - name: log-file-ingest
    source:
      type: File
      path: $in_file
      format:
        type: Line
    query: |-
      // Quine log pattern "%date %level [%mdc{pekkoSource:-NotFromActor}] [%thread] %logger - %msg%n%ex"
      WITH text.regexFirstMatch($that, "(^\\d{4}-\\d{2}-\\d{2} \\d{1,2}:\\d{2}:\\d{2},\\d{3}) (FATAL|ERROR|WARN|INFO|DEBUG) \\[(\\S*)\\] \\[(\\S*)\\] (\\S*) - (.*)") AS r WHERE r IS NOT NULL
      // 0: whole matched line
      // 1: date time string
      // 2: log level
      // 3: actor address. Might be inside of `org.apache.pekko.stream.Log(...)`
      // 4: thread name
      // 5: logging class
      // 6: Message
      WITH r, split(r[3], "/") as path,
              split(r[6], "(") as msgPts
      WITH r, path, msgPts, replace(COALESCE(split(path[2], "@")[-1], 'No host'),")","") as qh

      MATCH (actor), (msg), (class), (host)
      WHERE id(host)  = idFrom("host", qh)
        AND id(actor) = idFrom("actor", r[3])
        AND id(msg)   = idFrom("msg", r[0])
        AND id(class) = idFrom("class", r[5])

      SET host.address = split(qh, ":")[0],
          host.port = split(qh, ":")[-1],
          host.host = qh,
          host: Host

      SET actor.address = r[3],
          actor.id = replace(path[-1],")",""),
          actor.shard = path[-2],
          actor.type = path[-3],
          actor: Actor

      SET msg.msg = r[6],
          msg.path = path[0],
          msg.type = split(msgPts[0], " ")[0],
          msg.level = r[2],
          msg: Message

      SET class.class = r[5],
      class: Class

      WITH * CALL reify.time(datetime({date: localdatetime(r[1], "yyyy-MM-dd HH:mm:ss,SSS")})) YIELD node AS time

      CREATE (host)<-[:ON_HOST]-(actor)-[:SENT]->(msg),
             (actor)-[:OF_CLASS]->(class),
             (msg)-[:AT_TIME]->(time)

standingQueries: []

nodeAppearances:
  - predicate:
      propertyKeys: []
      knownValues: {}
      dbLabel: Actor
    label:
      type: Property
      key: id
      prefix: "Actor: "
  - predicate:
      propertyKeys: []
      knownValues: {}
      dbLabel: Message
    label:
      type: Property
      key: type
      prefix: "Message: "
  - predicate:
      propertyKeys: []
      knownValues: {}
      dbLabel: Host
    label:
      type: Property
      key: host
      prefix: "Host: "
  - predicate:
      propertyKeys: []
      knownValues: {}
      dbLabel: Class
    label:
      type: Property
      key: class
      prefix: "Class: "

quickQueries:
  - predicate:
      propertyKeys: []
      knownValues: {}
    quickQuery:
      name: Adjacent Nodes
      querySuffix: MATCH (n)--(m) RETURN DISTINCT m
      sort:
        type: Node
  - predicate:
      propertyKeys: []
      knownValues: {}
    quickQuery:
      name: Refresh
      querySuffix: RETURN n
      sort:
        type: Node
  - predicate:
      propertyKeys: []
      knownValues: {}
    quickQuery:
      name: Local Properties
      querySuffix: RETURN id(n), properties(n)
      sort:
        type: Text
  - predicate:
      propertyKeys: []
      knownValues: {}
      dbLabel: Actor
    quickQuery:
      name: Associated Host
      querySuffix: MATCH (n)-[:ON_HOST]->(host) RETURN DISTINCT host
      sort:
        type: Node
  - predicate:
      propertyKeys: []
      knownValues: {}
      dbLabel: Actor
    quickQuery:
      name: One Associated Message
      querySuffix: MATCH (n)-[:SENT]->(msg) RETURN DISTINCT msg LIMIT 1
      sort:
        type: Node
  - predicate:
      propertyKeys: []
      knownValues: {}
      dbLabel: Actor
    quickQuery:
      name: Associated Class
      querySuffix: MATCH (n)-[:OF_CLASS]->(class) RETURN DISTINCT class LIMIT 1
      sort:
        type: Node
  - predicate:
      propertyKeys: []
      knownValues: {}
      dbLabel: Message
    quickQuery:
      name: Associated Actor
      querySuffix: MATCH (actor)-[:SENT]->(n) RETURN DISTINCT actor LIMIT 1
      sort:
        type: Node
  - predicate:
      propertyKeys: []
      knownValues: {}
      dbLabel: Message
    quickQuery:
      name: Associated Host
      querySuffix: MATCH (host)<-[:ON_HOST]-(actor)-[:SENT]->(n) RETURN DISTINCT host LIMIT 1
      sort:
        type: Node
      edgeLabel: ON_HOST

sampleQueries:
  - name: Last 10 Nodes
    query: CALL recentNodes(10)
  - name: Get Actors
    query: "MATCH (a: Actor) RETURN a"
