finally eliminating log spam; tidying up the decoded log files's format

This commit is contained in:
Fate-JH 2023-12-19 05:04:01 -05:00
parent 284715d9a1
commit bb95e8fe91
3 changed files with 83 additions and 23 deletions

View file

@ -1,7 +1,7 @@
// Copyright (c) 2020 PSForever // Copyright (c) 2020 PSForever
package net.psforever.tools.decodePackets package net.psforever.tools.decodePackets
import java.io.{File, IOException} import java.io.{File, IOException, OutputStream, PrintStream}
import java.nio.charset.CodingErrorAction import java.nio.charset.CodingErrorAction
import java.nio.file.{Files, Path, Paths, StandardCopyOption} import java.nio.file.{Files, Path, Paths, StandardCopyOption}
import net.psforever.packet.PacketCoding import net.psforever.packet.PacketCoding
@ -26,6 +26,12 @@ case class Config(
object DecodePackets { object DecodePackets {
private val utf8Decoder = Codec.UTF8.decoder.onMalformedInput(CodingErrorAction.REPORT) private val utf8Decoder = Codec.UTF8.decoder.onMalformedInput(CodingErrorAction.REPORT)
private val normalSystemOut = System.out
private val outCapture: PrintStream = new PrintStream(new OutputStream() {
@Override
@throws(classOf[Exception])
def write(arg0: Int): Unit = { }
})
def main(args: Array[String]): Unit = { def main(args: Array[String]): Unit = {
val builder = OParser.builder[Config] val builder = OParser.builder[Config]
@ -141,6 +147,8 @@ object DecodePackets {
* and those that can not. * and those that can not.
* @param directory where the existing files may be found * @param directory where the existing files may be found
* @param files files to test for matching names * @param files files to test for matching names
* @see `filesWithSameNameAs`
* @see `getAllFilePathsFromDirectory`
* @return a tuple of file lists, comparing the param files against files in the directory; * @return a tuple of file lists, comparing the param files against files in the directory;
* the first are the files whose names match; * the first are the files whose names match;
* the second are the files whose names do not match * the second are the files whose names do not match
@ -157,6 +165,7 @@ object DecodePackets {
* and those that can not. * and those that can not.
* @param existingFiles files whose names are to test against * @param existingFiles files whose names are to test against
* @param files files to test for matching names * @param files files to test for matching names
* @see `lowercaseFileNameString`
* @return a tuple of file lists, comparing the param files against files in the directory; * @return a tuple of file lists, comparing the param files against files in the directory;
* the first are the files whose names match; * the first are the files whose names match;
* the second are the files whose names do not match * the second are the files whose names do not match
@ -184,6 +193,9 @@ object DecodePackets {
/** /**
* Enumerate over files found in the given directory for later. * Enumerate over files found in the given directory for later.
* @param directory where the files are found * @param directory where the files are found
* @see `Files.isDirectory`
* @see `Files.exists`
* @see `Paths.get`
* @return the discovered file paths * @return the discovered file paths
*/ */
private def getAllFilePathsFromDirectory(directory: String): Array[Path] = { private def getAllFilePathsFromDirectory(directory: String): Array[Path] = {
@ -209,6 +221,9 @@ object DecodePackets {
* @param temporaryDirectory destination directory where files temporarily exist while being written * @param temporaryDirectory destination directory where files temporarily exist while being written
* @param outDirectory destination directory where the files are stored after being written * @param outDirectory destination directory where the files are stored after being written
* @param readDecodeAndWrite next step of the file decoding process * @param readDecodeAndWrite next step of the file decoding process
* @see `Files.move`
* @see `Paths.get`
* @see `System.setOut`
*/ */
private def decodeFilesUsing( private def decodeFilesUsing(
files: Seq[File], files: Seq[File],
@ -224,7 +239,9 @@ object DecodePackets {
val tmpDirPath = temporaryDirectory.getAbsolutePath + File.separator val tmpDirPath = temporaryDirectory.getAbsolutePath + File.separator
val writer = writerConstructor(tmpDirPath, fileName) val writer = writerConstructor(tmpDirPath, fileName)
try { try {
System.setOut(outCapture)
readDecodeAndWrite(file, writer) readDecodeAndWrite(file, writer)
System.setOut(normalSystemOut)
writer.close() writer.close()
writer.getFileNames.foreach { fileNameWithExt => writer.getFileNames.foreach { fileNameWithExt =>
Files.move(Paths.get(tmpDirPath + fileNameWithExt), Paths.get(outDirPath + fileNameWithExt), StandardCopyOption.REPLACE_EXISTING) Files.move(Paths.get(tmpDirPath + fileNameWithExt), Paths.get(outDirPath + fileNameWithExt), StandardCopyOption.REPLACE_EXISTING)
@ -242,6 +259,11 @@ object DecodePackets {
* Read data from ASCII transcribed gcapy files. * Read data from ASCII transcribed gcapy files.
* @param file file to read * @param file file to read
* @param writer writer for output * @param writer writer for output
* @see `decodeFileContents`
* @see `File.getAbsolutePath`
* @see `Source.fromFile`
* @see `Source.getLines`
* @see `Using`
*/ */
private def preprocessed(file: File, writer: WriterWrapper): Unit = { private def preprocessed(file: File, writer: WriterWrapper): Unit = {
Using(Source.fromFile(file.getAbsolutePath)(utf8Decoder)) { source => Using(Source.fromFile(file.getAbsolutePath)(utf8Decoder)) { source =>
@ -253,6 +275,11 @@ object DecodePackets {
* Read data from gcapy files. * Read data from gcapy files.
* @param file file to read * @param file file to read
* @param writer writer for output * @param writer writer for output
* @see `decodeFileContents`
* @see `File.getAbsolutePath`
* @see `Source.fromFile`
* @see `Source.getLines`
* @see `Using`
*/ */
private def gcapy(file: File, writer: WriterWrapper): Unit = { private def gcapy(file: File, writer: WriterWrapper): Unit = {
Using(Source.fromString(s"gcapy -xa '${file.getAbsolutePath}'" !!)) { source => Using(Source.fromString(s"gcapy -xa '${file.getAbsolutePath}'" !!)) { source =>
@ -265,6 +292,10 @@ object DecodePackets {
* @param writer writer for output * @param writer writer for output
* @param lines raw packet data from the source * @param lines raw packet data from the source
* @throws java.io.IOException if writing data goes incorrectly * @throws java.io.IOException if writing data goes incorrectly
* @see `decodePacket`
* @see `isNestedPacket`
* @see `recursivelyHandleNestedPacket`
* @see `shortGcapyString`
* @return number of lines read from the source * @return number of lines read from the source
*/ */
@throws(classOf[IOException]) @throws(classOf[IOException])
@ -276,15 +307,13 @@ object DecodePackets {
if (linesToSkip > 0) { if (linesToSkip > 0) {
linesToSkip -= 1 linesToSkip -= 1
} else { } else {
val decodedLine = decodePacket( val header = shortGcapyString(line)
shortGcapyString(line), val decodedLine = decodePacket(header, line.drop(line.lastIndexOf(' ')))
line.drop(line.lastIndexOf(' '))
)
writer.write(decodedLine) writer.write(decodedLine)
val decodedLineText = decodedLine.text val decodedLineText = decodedLine.text
if (isNestedPacket(decodedLineText)) { if (isNestedPacket(decodedLineText)) {
// Packet with nested packets, including possibly other nested packets within e.g. SlottedMetaPacket containing a MultiPacketEx // Packet with nested packets, including possibly other nested packets within e.g. SlottedMetaPacket containing a MultiPacketEx
val nestedLinesToSkip = recursivelyHandleNestedPacket(decodedLineText, writer) val nestedLinesToSkip = recursivelyHandleNestedPacket(header, decodedLineText, writer)
// Gcapy output has duplicated lines for SlottedMetaPackets, so we can skip over those if found to reduce noise // Gcapy output has duplicated lines for SlottedMetaPackets, so we can skip over those if found to reduce noise
// The only difference between the original and duplicate lines is a slight difference in timestamp of when the packet was processed // The only difference between the original and duplicate lines is a slight difference in timestamp of when the packet was processed
linesToSkip = decodedLineText.indexOf("SlottedMetaPacket") match { linesToSkip = decodedLineText.indexOf("SlottedMetaPacket") match {
@ -309,10 +338,16 @@ object DecodePackets {
* @param writer writer for output * @param writer writer for output
* @param depth the number of layers to indent * @param depth the number of layers to indent
* @throws java.io.IOException if writing data goes incorrectly * @throws java.io.IOException if writing data goes incorrectly
* @see `decodePacket`
* @see `IOException`
* @see `isNestedPacket`
* @see `nested`
* @see `Regex.findAllIn`
* @return current indent layer * @return current indent layer
*/ */
@throws(classOf[IOException]) @throws(classOf[IOException])
private def recursivelyHandleNestedPacket( private def recursivelyHandleNestedPacket(
header: String,
decodedLine: String, decodedLine: String,
writer: WriterWrapper, writer: WriterWrapper,
depth: Int = 0 depth: Int = 0
@ -323,16 +358,15 @@ object DecodePackets {
var linesToSkip = 0 var linesToSkip = 0
while (matches.hasNext) { while (matches.hasNext) {
val packet = matches.next() val packet = matches.next()
for (i <- depth to 0 by -1) { for (_ <- depth until 0 by -1) {
if (i == 0) writer.write("> ") writer.write(str = "-")
else writer.write("-")
} }
val nextDecoded = decodePacket(decodedLine, packet) writer.write(str = "> ")
val nextDecoded = nested(decodePacket(header, packet))
val nextDecodedLine = nextDecoded.text val nextDecodedLine = nextDecoded.text
writer.write(nextDecoded) writer.write(nextDecoded)
writer.newLine()
if (isNestedPacket(nextDecodedLine)) { if (isNestedPacket(nextDecodedLine)) {
linesToSkip += recursivelyHandleNestedPacket(nextDecodedLine, writer, depth + 1) linesToSkip += recursivelyHandleNestedPacket(header, nextDecodedLine, writer, depth + 1)
} }
linesToSkip += 1 linesToSkip += 1
} }
@ -366,14 +400,19 @@ object DecodePackets {
/** /**
* Actually decode the packet data. * Actually decode the packet data.
* @param hexString raw packet data * @param hexString raw packet data
* @see `ByteVector.fromValidHex`
* @see `DecodeError`
* @see `DecodedPacket`
* @see `PacketCoding.decodePacket`
* @return decoded packet data * @return decoded packet data
*/ */
private def decodePacket(header: String, hexString: String): PacketOutput = { private def decodePacket(header: String, hexString: String): PacketOutput = {
val byteVector = ByteVector.fromValidHex(hexString) val byteVector = ByteVector.fromValidHex(hexString)
PacketCoding.decodePacket(byteVector) match { val result = PacketCoding.decodePacket(byteVector) match {
case Successful(value) => DecodedPacket(header, value.toString.replace(",", ", ")) case Successful(value) => DecodedPacket(Some(header), value.toString.replace(",", ", "))
case Failure(cause) => DecodeError(header, s"Decoding error '${cause.toString}'") case Failure(cause) => DecodeError(Some(header), s"Decoding error '${cause.toString}'")
} }
result
} }
/** produce a wrapper that writes decoded packet data */ /** produce a wrapper that writes decoded packet data */
@ -381,6 +420,19 @@ object DecodePackets {
DecodeWriter(directoryPath, fileName) DecodeWriter(directoryPath, fileName)
} }
/**
* When nested, a normal properly decoded packet does not print header information.
* The header usually contains the original encoded hexadecimal string.
* @param in decoded packet data
* @return decoded packet data, potentially without header information
*/
private def nested(in: PacketOutput): PacketOutput = {
in match {
case DecodedPacket(_, text) if !in.text.contains("Decoding error") => DecodedPacket(header=None, text)
case _ => in
}
}
/** produce a wrapper that writes decoded packet data and writes decode errors to a second file */ /** produce a wrapper that writes decoded packet data and writes decode errors to a second file */
private def errorWriter(directoryPath: String, fileName: String): WriterWrapper = { private def errorWriter(directoryPath: String, fileName: String): WriterWrapper = {
DecodeErrorWriter(directoryPath, fileName) DecodeErrorWriter(directoryPath, fileName)

View file

@ -2,10 +2,10 @@
package net.psforever.tools.decodePackets package net.psforever.tools.decodePackets
trait PacketOutput { trait PacketOutput {
def header: String def header: Option[String]
def text: String def text: String
} }
final case class DecodedPacket(header: String, text: String) extends PacketOutput final case class DecodedPacket(header: Option[String], text: String) extends PacketOutput
final case class DecodeError(header: String, text: String) extends PacketOutput final case class DecodeError(header: Option[String], text: String) extends PacketOutput

View file

@ -19,10 +19,19 @@ final case class DecodeWriter(directoryPath: String, fileName: String) extends W
def write(str: String): Unit = log.write(str) def write(str: String): Unit = log.write(str)
def write(data: PacketOutput): Unit = { def write(data: PacketOutput): Unit = {
log.write(data.header) data.header
log.newLine() .collect { str =>
log.write(data.text) log.write(str)
log.newLine() log.newLine()
log.write(data.text)
log.newLine()
Some(str)
}
.orElse {
log.write(data.text)
log.newLine()
None
}
} }
def newLine(): Unit = log.newLine() def newLine(): Unit = log.newLine()
@ -47,7 +56,6 @@ final case class DecodeErrorWriter(directoryPath: String, fileName: String)
def write(data: PacketOutput): Unit = { def write(data: PacketOutput): Unit = {
log.write(data) log.write(data)
log.newLine()
data match { data match {
case error: DecodeError => case error: DecodeError =>
errorLog.write(error) errorLog.write(error)