Solved A scala program which show syslog-ng messages as stored in postgresql , in a gui

(A.I. was used in the coding & coloring process)

Colored using bbcode , as scala is yet unknown to xenforo

Rich (BB code):
// Note you can provide commandline argument to filter

package my.scalafx

//--- SYSTEM LOGISTICS & DRIVER IMPORTS
import cats.effect.IO
import cats.effect.unsafe.implicits.global
import doobie.util.transactor.Transactor
import doobie.syntax.string._
import doobie.syntax.connectionio._
import doobie.free.connection.ConnectionIO
import doobie.util.fragment.Fragment
import scalafx.application.JFXApp
import scalafx.application.JFXApp.PrimaryStage
import scalafx.application.Platform
import scalafx.collections.ObservableBuffer
import scalafx.scene.Scene
import scalafx.scene.control.{TableColumn, TableView}
import scalafx.scene.layout.BorderPane
import scalafx.beans.property.StringProperty
import java.util.regex.Pattern
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter

//--- RELATIONAL SCHEMA ROW BLUEPRINT
case class LogRow(
  host: Option[String],
  facility: Option[String],
  priority: Option[String],
  tag: Option[String],
  datetime: Option[String],
  program: Option[String],
  msg: Option[String]
)

//--- OPTIMIZED UI MODEL FOR FLUID RENDERING
case class UiLogRow(
  host: String,
  facility: String,
  priority: String,
  tag: String,
  datetime: String,
  program: String,
  msg: String
)

object ScalaFXHelloWorld extends JFXApp {

  // --- UNIX SPECIFICATION STATIC DICTIONARIES ---
  private val Facilities = Array(
    "kernel", "user", "mail", "daemon", "auth", "syslog", "lpr", "news",
    "uucp", "cron", "authpriv", "ftp", "ntp", "audit", "alert", "clock",
    "local0", "local1", "local2", "local3", "local4", "local5", "local6", "local7"
  )

  private val Severities = Array(
    "emerg", "alert", "crit", "err", "warning", "notice", "info", "debug"
  )

  // --- PARSING & SCRUBBING UTILITIES ---
  private val ProgramExtractorPattern = Pattern.compile("^(?:\\S+)\\s+(?:\\S+)\\s+(?<prog>[a-zA-Z0-9_\\-]+)(?:\\[[0-9]+\\])?\\s+")
  private val InputFormatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME
  private val OutputFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")

  // --- DRY EXTRACTION & VALUE CONVERTER FUNCTIONS ---
  private def getOrBlank(f: Option[String]): String = f.getOrElse("").trim

  private def createCell(v: TableColumn.CellDataFeatures[UiLogRow, String], extractor: UiLogRow => String): StringProperty =
    new StringProperty(v.value, "", extractor(v.value))

  private def formatTimestamp(rawDateTime: String): String = try {
    val s = rawDateTime.trim
    if (s.isEmpty) "" else ZonedDateTime.parse(s.replaceAll("TT", "T"), InputFormatter).format(OutputFormatter)
  } catch { case _: Exception => rawDateTime }

  private def decodeHexTag(hexStr: String): String = try {
    val h = hexStr.trim.toLowerCase
    if (h.isEmpty) "" else {
      val n = Integer.parseInt(h, 16)
      val fIdx = n >> 3
      val sIdx = n & 7
      val fText = if (fIdx >= 0 && fIdx < Facilities.length) Facilities(fIdx) else s"unknown($fIdx)"
      val sText = if (sIdx >= 0 && sIdx < Severities.length) Severities(sIdx) else s"unknown($sIdx)"
      s"$fText.$sText"
    }
  } catch { case _: NumberFormatException => hexStr }

  private def resolveProgramName(rawProg: Option[String], rawMsg: Option[String]): String = {
    val p = getOrBlank(rawProg)
    val m = getOrBlank(rawMsg)
    if (p == "1" || p.isEmpty) {
      val mat = ProgramExtractorPattern.matcher(m)
      if (mat.find()) mat.group("prog") else p
    } else p
  }

  // --- DATABASE NETWORK DATA LAYER ---
  val xa: Transactor[IO] = Transactor.fromDriverManager[IO](
    driver = "org.postgresql.Driver",
    url = "jdbc:postgresql://localhost:5432/logs",
    user = "x",
    password = "x",
    logHandler = None
  )

  // Dynamically shifts database processing matching across tables
  def fetchLogs(filterTerm: Option[String]): ConnectionIO[List[LogRow]] = {
    val baseQuery = fr"SELECT host, facility, priority, tag, datetime, program, msg FROM logs"
    
    val filterFragment = filterTerm match {
      case Some(term) if term.trim.nonEmpty =>
        val matchPattern = s"%${term.trim}%"
        fr"WHERE host ILIKE $matchPattern OR facility ILIKE $matchPattern OR priority ILIKE $matchPattern OR tag ILIKE $matchPattern OR datetime ILIKE $matchPattern OR program ILIKE $matchPattern OR msg ILIKE $matchPattern"
      case _ => 
        Fragment.empty
    }
    
    val orderFragment = fr"ORDER BY datetime DESC"
    
    (baseQuery ++ fr" " ++ filterFragment ++ fr" " ++ orderFragment).query[LogRow].to
  • } // --- INTERFACE CANVAS STRUCTURAL OBJECTS --- val tableItems: ObservableBuffer[UiLogRow] = ObservableBuffer[UiLogRow]() val tableView = new TableView[UiLogRow](tableItems) { columns ++= Seq( new TableColumn[UiLogRow, String] { text = "Host"; cellValueFactory = c => createCell(c, _.host) }, new TableColumn[UiLogRow, String] { text = "Facility"; cellValueFactory = c => createCell(c, _.facility) }, new TableColumn[UiLogRow, String] { text = "Priority"; cellValueFactory = c => createCell(c, _.priority) }, new TableColumn[UiLogRow, String] { text = "Tag"; prefWidth = 80; cellValueFactory = c => createCell(c, _.tag) }, new TableColumn[UiLogRow, String] { text = "Datetime"; prefWidth = 200; cellValueFactory = c => createCell(c, _.datetime) }, new TableColumn[UiLogRow, String] { text = "Program"; prefWidth = 100; cellValueFactory = c => createCell(c, _.program) }, new TableColumn[UiLogRow, String] { text = "Message"; prefWidth = 2000; cellValueFactory = c => createCell(c, _.msg) } ) } tableView.columnResizePolicy = TableView.UnconstrainedResizePolicy // --- SCENE & MAIN VIEWPORT WRAPPERS --- stage = new PrimaryStage { title = "Database Log Viewer" width = 1600 height = 800 scene = new Scene { root = new BorderPane { center = tableView } } } // --- RUNTIME SCHEDULING INTERFACE MANAGEMENT --- val databaseTask: IO[Unit] = for { cliArg <- IO.delay(parameters.raw.headOption) results <- fetchLogs(cliArg).transact(xa) processedRows <- IO.delay { results.map { r => val resolvedProg = resolveProgramName(r.program, r.msg) UiLogRow( host = getOrBlank(r.host), facility = getOrBlank(r.facility), priority = getOrBlank(r.priority), tag = decodeHexTag(getOrBlank(r.tag)), datetime = formatTimestamp(getOrBlank(r.datetime)), program = resolvedProg, msg = getOrBlank(r.msg).drop(42) ) }.filterNot { r => r.program == "devd" || r.program == "influxd" } } _ <- IO.delay { Platform.runLater { tableItems.clear() tableItems ++= processedRows } } } yield () databaseTask.unsafeRunAndForget() }
 
Only 153 lines & type-safe.

Todo , give explenation of scala code.

Sample output,



test.png
 
That's actually quite impressive in only 135 lines, given what it's doing. It's pretty readable too, I gave it a quick scan through and can mostly follow what it's doing.
 
Scripts used :

Count number of lines in scala file

sh:
grep -c '[^[:space:]]' $1


Color scala file (input.md)

Python:
#!/usr/local/bin/python

# I was created by A.I.

import re

# Combined token pattern prioritizing comments and strings to protect them from keyword replacement
TOKEN_RE = re.compile(
    r"(?P<COMMENT>//.*)|"
    r"(?P<STRING>\"\"\"[\s\S]*?\"\"\"|\"[^\"]*\")|"
    r"\b(?P<KEYWORD>package|import|object|class|case|def|val|var|private|new|extends|with|Option|Array|String)\b|"
    r"\b(?P<TYPE>IO|Transactor|ConnectionIO|JFXApp|PrimaryStage|Platform|ObservableBuffer|Scene|TableColumn|TableView|BorderPane|StringProperty|Pattern|ZonedDateTime|DateTimeFormatter|LogRow|UiLogRow|ScalaFXHelloWorld|Facilities|Severities)\b"
)

def colorize(match):
    if match.group("COMMENT"):
        return f"[COLOR=#60a0b0]{match.group('COMMENT')}[/COLOR]"
    elif match.group("STRING"):
        return f"[COLOR=#40a070]{match.group('STRING')}[/COLOR]"
    elif match.group("KEYWORD"):
        return f"[COLOR=#007020]{match.group('KEYWORD')}[/COLOR]"
    elif match.group("TYPE"):
        return f"[COLOR=#0e84b5]{match.group('TYPE')}[/COLOR]"
    return match.group(0)

with open("input.md", "r") as f:
    content = f.read()

# Strip out markdown ticks cleanly across multi-line breaks
content = re.sub(r"^```[a-zA-Z0-9]*\n", "", content, flags=re.MULTILINE)
content = re.sub(r"\n```$", "", content, flags=re.MULTILINE)

# Execute the syntax matching substitution pass
colored_content = TOKEN_RE.sub(colorize, content)

# XenForo requires uppercase CODE=rich block wraps
output = colored_content

with open("output.bbcode", "w") as out:
    out.write("\n".join(output) + "\n")
 
Back
Top