Setting Logback Configuration file Programmatically - sbt

I'm using the sbt run command to run my project. My project uses the Logback logging mechanism and if I would like to enable logging, then I have to use the following command:
sbt -Dlogback.configurationFile=/path/to/log/file/app-logger.xml run
Is there a way that I could set this programmatically? I mean I would like to just say
sbt run
and it picks up automagically the app-logger.xml by itself via the application.

This is how I do it!
def loadLogger() = Option(System.getProperty("logback.configurationFile")) match {
case Some(logXml) =>
logger.info(s"using logger $logXml")
case None =>
val path = s"${System.getProperty("user.dir")}/conf/app-logger.xml"
System.setProperty("logback.configurationFile", path)
logger.info(s"using logger $path")
}

Related

Delpoy NetCore application using Azure DevOps

I create release pipeline on Azure DevOps server and i have a some problem.
How i can change properties in .net core configuration file (appsettings.EnvName.json).
When I create application on framework I had parameters.xml where I set XPath to value, default value and property name. And on pipeline I set key-value. But on net core app this method don't work =)
I want to use about the same approach. What would I indicate the path to the value and its value. For example:
ConnectionStrings.Db1="Server={DB1.Server};Database={DB1.DbName};Trusted_Connection = True;"
ConnectionStrings.Db2="Server={DB2.Server};Database={DB2.DbName};Trusted_Connection = True;"
Now I have added a step to execute an arbitrary powershell script on a remote server
$jsonFile = 'appsettings.Template.json'
$jsonFileOut = 'appsettings.Production.json'
$configValues =
'ConnectionStrings.Db1="Server={DB1.Server};Database={DB1.DbName};Trusted_Connection = True;"',
'ConnectionStrings.Db2="Server={DB2.Server};Database={DB2.DbName};Trusted_Connection = True;"'
$config = Get-Content -Path $jsonFile | ConvertFrom-Json
ForEach ($item in $configValues)
{
$kv = $item -split "="
Invoke-Expression $('$config.' + $kv[0] + '="' + $kv[1] + '"')
}
$config | ConvertTo-Json | Out-File $jsonFileOut
But I don’t really like this solution, how can I do the same in a more beautiful way
dotnet core handles this in a different way. Full framework based on app.config transformation. It means that you defined one file which later was trasnformed for given build configuration (like Debug, Release, or your own). In dotnet core you define appsettings.json for each environment. This works very well because all settings are in your compiled app. And then at runtime bases on ASPNETCORE_ENVIRONMENT environment variable a proper settings is selected. Thus you may have one package for your all environments without recompilation. To benefit from that you must define file per each enviroment, but this is not transformation. This is full file.
For instance file for your local development may look like this:
{
"ConnectionStrings": {
"BloggingDatabase": "Server=(localdb)\\mssqllocaldb;Database=EFGetStarted.ConsoleApp.NewDb;Trusted_Connection=True;"
},
}
And file for your dev enviroment appsettings.dev.json like this:
{
"ConnectionStrings": {
"BloggingDatabase": "Server=102.10.10.12\\mssqllocaldb;Database=EFGetStarted.ConsoleApp.NewDb;Trusted_Connection=True;"
},
}
And then to configure loading this file you have to have configured Startup method:
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
this.Configuration = builder.Build();
}
This will load all your appsettings file and later use proper file based on enviroment variable.
To set this variable you may use this command in command prompt setx ASPNETCORE_ENVIRONMENT Dev or this in Powershell [Environment]::SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "Dev", "Machine")
I hope it help you understand how settings works on dotnet core. If you need more guidance please check this links:
Configuration in ASP.NET Core
Use multiple environments in ASP.NET Core
To sum up you don't need to change your settings in release pipeline. You need to preapre full file per enviromnet where you are going to host your app. You can be interested in replacing some values in file based on variables in your pipeline. You can consider few options here like
token replacement
JSON variable substitution example
This is usefult when you don't want to keep your secrets directly in source code.
EDIT
If you want to replace values in you appsettings file one of the option is token replace. For this you must first instead of values keep token in your file. For instance #{SomeVariable}# will be replaced with value of SomeVariable` from your pipeline for this confirguration of token replace task.

Is it possible to disable publish without disabling publishLocal in sbt?

I have an sbt project where docker:publishLocal will create a docker image on my machine for testing, and docker:publish will publish the image to a repository and also publish jar files from the build to a repository.
If my project is a snapshot, I would like to disable publishing to the repositories, while still being able to build the local image.
ThisBuild / publishArtifact := ! isSnapshot.value
does the right thing for the publish command, but it also disables publishLocal.
I want to write something like
if (isSnapshot.value) {
publish := { }
}
but that gives me an error that I do not understand at all:
[info] Loading project definition from /Users/dev/project
/Users/dev/build.sbt:1: error: type mismatch;
found : Unit
required: sbt.internal.DslEntry
if (isSnapshot.value) {
^
Past experience dictates that redefining publish to conditionally call the original version won't work, as
publish := {
if (!isSnapshot.value) publish.value
}
gives warnings that the task is always evaluated.
Is there a way to do this?
The problem with this code is that it evaluates publish.value regardless of the if structure. I recommend reading the documentation on task dependencies. If you want to "delay" the evaluation of a task in one of the if branches, you need to use dynamic task definition:
publish := Def.taskDyn {
if (isSnapshot.value)
Def.task {} // doing nothing
else
Def.task { publish.value } // could be written as just publish
}.value
But apart from fixing your code, you should be aware that there is a special setting for the functionality you want, it's called skip:
publish/skip := isSnapshot.value
Another thing to notice, is the scoping. If you want to override docker:publish, which is the same as Docker/publish in the new syntax, you should add this Docker/ scope prefix to every mention of publish in the code above.

SBT: Add dependencies to project at runtime

There is sbt project declaration
lazy val myProject = (Project("myProject", file("someRoot"))
enablePlugins ...
settings (...)
It has taskKey that extracts some dependencies to file system.
My problem is that for the moment of loading SBT I can't determine all the dependencies, it could be done only after private Command Alias is executed
addCommandAlias("resolveDependencies", "; resolveDependenciesTask; TODO: update myProject dependencies and reload it")
Is there anyway to do that?
Actually, disregard my comment on your question. You can use a command to modify the state of the build, so after you run it, the changes it made stay.
Something along these lines:
// in your build.sbt
commands += Command.command("yourCustomCommand")(state =>
Project.extract(state).append(
Seq(libraryDependencies += // settings you want to modify
"com.lihaoyi" % "ammonite-repl" % "0.5.7" cross CrossVersion.full),
state))
Then call it with sbt yourCustomCommand.
The state instance you return from the command becomes the new state of the build, i.e. if you've added some dependencies, the build will see them.

Passing JVM option to forked test in SBT

I am trying to use a JVM option in a forked test, which has been set externally to SBT prior to its launch. I'm also setting additional JVM options like so:
javaOptions in ThisBuild ++= List("-Xmx3072m")
to my understanding, based on the SBT documentation the JVM options provided to the SBT process should be available to the forked process:
By default, a forked process uses the same Java and Scala versions being used for the build and the working directory and JVM options of the current process.
However, I can't seem to retrieve those "external" JVM options in the forked tests, i.e. System.getProperty("foo") will always return null. Given that I am trying to pass along a password, I can't set it directly in the build file. My questions therefore are:
is there an SBT task / key to access the JVM options passed to the JVM in which SBT is running? That way I would attempt to add the key to the javaOptions
is there any other means by which to pass external Java Options to a forked test?
You may control your options with testGrouping. Below copy'n'paste from one of my projects. It properly handles hierarchical projects and root project without tests too. Options are merged from javaOptions in run and test.options file. This allow modify arguments without project reloading. That project has load time more then minute. So I use test.options to fast switch between production and debug mode with -Xrunjdwp:transport=dt_...
testGrouping in Test <<= (definedTests in Test, javaOptions in run, baseDirectory in LocalRootProject) map { (tests, javaOptions, baseDirectory) ⇒
if (tests.nonEmpty) {
val testOptionsFile = baseDirectory / "test.options"
val externalOptions = if (testOptionsFile.exists()) {
val source = scala.io.Source.fromFile(testOptionsFile)
val options = source.getLines().toIndexedSeq
source.close()
options
} else Nil
tests map { test ⇒
new Tests.Group(
name = test.name,
tests = Seq(test),
// runPolicy = Tests.InProcess)
runPolicy = Tests.SubProcess(javaOptions = javaOptions ++ externalOptions))
}
} else {
Seq(new Tests.Group(
name = "Empty",
tests = Seq(),
runPolicy = Tests.InProcess))
}
},

trying to use log4j.xml file within WinRun4j

has anyone tried to use a log4j.xml reference within a WinRun4j service configuration. here is a copy of my service.ini file. I have tried many configuration combinations. this is just my latest attempt
service.class=org.boris.winrun4j.MainService
service.id=SimpleBacnetIpDataTransfer
service.name=Simple Backnet IP DataTransfer Service
service.description=This is the service for the Simple Backnet IP DataTransfer.
service.startup=auto
classpath.1=C:\Inbox\DataTransferClient-1.0-SNAPSHOT-jar-with-dependencies.jar
classpath.2=WinRun4J.jar
classpath.3=C:\Inbox\log4j-1.2.16.jar
arg.1=C:\Inbox\DataTransferClient.xml
log=C:\WinRun4J-Service\SimpleBacnetIpDataTransfer\NBP-DT-service.log
log.overwrite=true
log.roll.size=10MB
[MainService]
class=com.shiftenergy.ws.App
vmarg.1=-Xdebug
vmarg.2=-Xnoagent
vmarg.3=-Xrunjdwp:transport=dt_socket,address=8787,server=y,suspend=n
vmarg.4=-Dlog4j.configuration=file:C:\Inbox\log4j.xml
within the log4j.xml file, there is reference to a log file for when the application runs. if I run the java -jar -Dlog4j.configuration=file:C:\Inbox\log4j.xml ...., the log file is created accordingly. if I register my service and start the service, the log file does not get created.
has anyone had success using the -D log4j configuration, using winrun4j?
thanks
I think that you provided the vmarg.4 parameter incorrectly. In your case it has to be like:
vmarg.4=-Dlog4j.configurationFile=[Path for log4j.xml]
I am also using the same and in my case, it is working perfectly fine. Please see below example:
vmarg.1=-Dlog4j.configurationFile=.\log4j2.xml
Have you tried setting the path in your code instead:
System.setProperty("log4j.configurationFile", "config/log4j.xml");
I'm using a relative path to a folder named config that contains log4j.xml. An absolute path is not recommended, but may work as well.
Just be sure to set this before making any calls to log4j, including any log4j config settings or static method calls!
System.setProperty("log4j.configurationFile", "config/log4j.xml");
final Logger log = Logger.getLogger(Main.class);
log.info("Starting up");
I didn't specify the log4j path in the ini file, only placed log4j.xml file at the same place the jar was placed.
Also without specify the
System.setProperty("log4j.configurationFile", "config/log4j.xml");
In the Java project it was stored in (src/main/resources) and will be included in the jar, but it will not be that one used if placed outside the jar.

Resources