Xml for logging

I’m writing an application where I would like to log calls to methods, by creating an object for each run of the method call, where the object’s class has a field for input arguments and another field for the return value.

I’ve seen in Chapter 28 of Staircase Book, “Working with XML”, a way of writing a single xml node to a file using scala.xml.XML.save.

However, my program runs at different times so I would like to log the data over time.
When I want to review the data, I would like to see it in an HTML table, where each <td> corresponds to one element in the serialized data (where element of the XML maps to one field in the case class).

Is there a best and simple way of doing this? The data will always be well-formed and just used internally so I don’t want to overcomplicate it.

Also, is this going to be possible, because appending to an XML file might be problematic because it needs to have a closed tag? eg

<catalog>
 <functioncall>
   <time>xx</time>
   <input>xx</input>
   <result>xx</result>
  </functioncall>
 <functioncall>
   <time>xx</time>
   <input>xx</input>
   <result>xx</result>
  </functioncall>
...

Edit:
Have found this in the meantime

val catalog = scala.xml.XML.loadFile(myFile.getAbsolutePath)

import scala.xml._

def addChild(n: Node, newChild: Node) = n match {
  case Elem(prefix, label, attribs, scope, child @ _*) =>
    Elem(prefix, label, attribs, scope, true, child ++ newChild : _*)
  case _ => throw new Exception("Can only add children to elements!")
}

addChild(catalog, this.toXML)
scala.xml.XML.save(myFile.getAbsolutePath, catalog)

but not sure if the best way.

I don’t think XML is appropriate for your use case. Yes, you’ll need to have a single outer element with a closing tag for well-formed XML. And with the approach you’ve outlined, you’ll parse and re-render your full log for every single entry you add. This sounds pretty prohibitive.

For maintaining the actual log, I’d look into some other format that allows plain append, like CSV or some application of scodec. For inspection, you can then parse the log and convert it to XML/HTML as needed.

1 Like

Thanks - I’ll go for CSV.

It’s a shame since XML does seem to give detail to the log. However, the scala XML library, as far as I’m aware, cannot save headers such as xsl details for xslt (it can do a DOCTYPE). You can if you read in the whole file, and then write out a string with these details prepended but this is a hassle. Having the xlst details and the xml data does what I need in one go.

But writing out an XHTML file should be easy given XML is in-built to scala.

For the file ops, does using a pattern such as:

def using[A <: { def close(): Unit }, B](resource: A)(f: A => B): B =
   try {
     f(resource)
   } finally {
       resource.close()
     }

slow things down since you close a file or resource every time or is this better since its safer and doesn’t leave things open?

Also I have read that A <: SomeClass is now not the preferred way. How would the above be written with the A : T context-bound style?

I certainly wouldn’t reopen and close the log file for every single entry. (You might consider flushing for each entry, though.) But you definitely should ensure that the file is always closed once processing is finished. There’s various support for automated resource management available:

  • scala-arm provides a standalone monadic approach. Unfortunately there’s no version for Scala 2.13 as of now and it looks like it’s going to be abandoned due to…
  • Using, which has been introduced in the Scala 2.13 standard lib. It comes with a more specialized, custom API.
  • If you want to buy into the Cats eco system, there’s Resource.

All of these APIs are type class based, i.e. they use/blend in with context bounds.

You can do this by rendering the computation through treelog or putting it through a sourcecode wrapped loan pattern, like https://github.com/tersesystems/blindsight/blob/master/example/src/main/scala/example/flow/FlowMain.scala