Skip to content

Commit

Permalink
Save metadata files on validate exception
Browse files Browse the repository at this point in the history
  • Loading branch information
kjonescertinia committed Dec 12, 2024
1 parent 25d198e commit 154f111
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 10 deletions.
46 changes: 44 additions & 2 deletions jvm/src/main/scala/com/nawforce/apexlink/org/OrgInfo.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,18 @@
package com.nawforce.apexlink.org

import com.nawforce.apexlink.org.OPM.OrgImpl
import com.nawforce.pkgforce.diagnostics.{Diagnostic, ERROR_CATEGORY, Issue, MISSING_CATEGORY}
import com.nawforce.pkgforce.path.PathLocation
import com.nawforce.pkgforce.diagnostics.{
Diagnostic,
ERROR_CATEGORY,
Issue,
LoggerOps,
MISSING_CATEGORY
}
import com.nawforce.pkgforce.path.{Location, PathLike, PathLocation}

import java.io.{File, PrintWriter, StringWriter}
import java.nio.file.Files
import scala.collection.compat.immutable.ArraySeq
import scala.util.DynamicVariable

/** Access to the 'current' org, this should be deprecated now we have the OPM hierarchy.
Expand All @@ -42,4 +51,37 @@ object OrgInfo {
log(new Issue(pathLocation.path, Diagnostic(MISSING_CATEGORY, pathLocation.location, message)))
}

/** Log an exception during processing. If at least one path is provided this logs the
* exception against the first. The files are copied to a temporary directory to aid debugging.
*/
private[nawforce] def logException(ex: Throwable, paths: ArraySeq[PathLike]): Unit = {
if (paths.isEmpty) {
LoggerOps.info("Exception reported against no paths", ex)
return
}

try {
val writer = new StringWriter
writer.append("Validation failed: ")
val tempDir = Files.createTempDirectory("apex-ls-log")
paths.foreach(path => {
val from = new File(path.toString).toPath
if (Files.exists(from)) {
Files.copy(
from,
tempDir.resolve(from.getFileName),
java.nio.file.StandardCopyOption.REPLACE_EXISTING
)
}
})
writer.append("log directory ")
writer.append(tempDir.toString)
writer.append('\n')
ex.printStackTrace(new PrintWriter(writer))
log(Issue(ERROR_CATEGORY, PathLocation(paths.head, Location.empty), writer.toString))
} catch {
case ex: Throwable =>
LoggerOps.info("Failed to log an exception", ex)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import com.nawforce.pkgforce.parsers.{CLASS_NATURE, INTERFACE_NATURE, Nature}
import com.nawforce.pkgforce.path.{Location, PathLike, PathLocation, UnsafeLocatable}

import java.io.{PrintWriter, StringWriter}
import java.nio.file.Files
import scala.collection.immutable.ArraySeq
import scala.collection.mutable

Expand Down Expand Up @@ -431,14 +432,7 @@ trait TypeDeclaration extends AbstractTypeDeclaration with Dependent with PreReV
try {
validate()
} catch {
case ex: Throwable =>
val writer = new StringWriter
writer.append("Validation failed")
writer.append(": ")
ex.printStackTrace(new PrintWriter(writer))
OrgInfo.log(
Issue(ERROR_CATEGORY, PathLocation(paths.head, Location.empty), writer.toString)
)
case ex: Throwable => OrgInfo.logException(ex, paths)
}
}

Expand Down
88 changes: 88 additions & 0 deletions jvm/src/test/scala/com/nawforce/apexlink/org/OrgInfoTest.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* Copyright (c) 2024 Certinia Inc. All rights reserved.
*/
package com.nawforce.apexlink.org

import com.nawforce.apexlink.TestHelper
import com.nawforce.pkgforce.diagnostics.LoggerOps.{INFO_LOGGING, NO_LOGGING}
import com.nawforce.pkgforce.diagnostics.{Logger, LoggerOps}
import com.nawforce.pkgforce.path.PathLike
import com.nawforce.runtime.FileSystemHelper
import org.scalatest.funsuite.AnyFunSuite

import java.io.{File, StringWriter}
import scala.collection.immutable.ArraySeq

class OrgInfoTest extends AnyFunSuite with TestHelper {

final class CaptureLogger extends Logger {
val writer: StringWriter = new StringWriter()

override def info(message: String): Unit = {
writer.append(s"INFO: $message\n")
}

override def debug(message: String): Unit = {
writer.append(s"DEBUG: $message\n")
}

override def trace(message: String): Unit = {
writer.append(s"TRACE: $message\n")
}
}

test("Log exception without files creates info message") {
FileSystemHelper.run(Map()) { root: PathLike =>
createOrg(root)

val captureLogger = new CaptureLogger
val oldLogger = LoggerOps.setLogger(captureLogger)
LoggerOps.setLoggingLevel(INFO_LOGGING)

OrgInfo.logException(new Exception("Hello"), ArraySeq())

assert(captureLogger.writer.toString.startsWith("INFO: Exception reported against no paths"))
assert(captureLogger.writer.toString.contains("INFO: java.lang.Exception: Hello"))

LoggerOps.setLoggingLevel(NO_LOGGING)
LoggerOps.setLogger(oldLogger)

}
}

test("Log exception captures files") {
FileSystemHelper.runTempDir(
Map("a.txt" -> "a.txt", "dir1/b.txt" -> "b.txt", "dir1/dir2/c.txt" -> "c.txt")
) { root: PathLike =>
createOrg(root)

val captureLogger = new CaptureLogger
val oldLogger = LoggerOps.setLogger(captureLogger)
LoggerOps.setLoggingLevel(INFO_LOGGING)

try {

withOrg { _ =>
OrgInfo.logException(
new Exception("Hello"),
ArraySeq(root.join("a.txt"), root.join("dir1/b.txt"), root.join("dir1/dir2/c.txt"))
)
}

assert(captureLogger.writer.toString.isEmpty)
assert(getMessages().contains("/a.txt: Error: line 1: Validation failed: log directory"))
assert(getMessages().contains("java.lang.Exception: Hello"))

val tmpDir = getMessages().split("\n").head.split("directory").last.trim
val tmpDirPath = new File(tmpDir)
assert(tmpDirPath.isDirectory)
assert(tmpDirPath.listFiles().map(_.getName).toSet == Set("a.txt", "b.txt", "c.txt"))

} finally {
LoggerOps.setLoggingLevel(NO_LOGGING)
LoggerOps.setLogger(oldLogger)
}
}
}

}

0 comments on commit 154f111

Please sign in to comment.