Spring-boot: Ensure all properties map nicely to yaml

Created on 15 Mar 2018  路  17Comments  路  Source: spring-projects/spring-boot

logging.file=logs/app.log
logging.file.max-history=20 
logging.file.max-size=50MB 

how to config these properties in yml file

#log 
logging:
  file:  // how to config
    max-history: 30
    max-size: 50MB

Most helpful comment

I think I finally understand the issue here. A sample would have been very useful. As would have including the full stack trace.

Given the following YAML:

logging:
  file: logs/app.log
    max-history: 20
    max-size: 50MB

The following exception is raised:

java.lang.IllegalStateException: Failed to load property source from location 'classpath:/application.yml'
    at org.springframework.boot.context.config.ConfigFileApplicationListener$Loader.load(ConfigFileApplicationListener.java:481)
    at org.springframework.boot.context.config.ConfigFileApplicationListener$Loader.loadForFileExtension(ConfigFileApplicationListener.java:446)
    at org.springframework.boot.context.config.ConfigFileApplicationListener$Loader.load(ConfigFileApplicationListener.java:419)
    at org.springframework.boot.context.config.ConfigFileApplicationListener$Loader.load(ConfigFileApplicationListener.java:329)
    at org.springframework.boot.context.config.ConfigFileApplicationListener.addPropertySources(ConfigFileApplicationListener.java:205)
    at org.springframework.boot.context.config.ConfigFileApplicationListener.postProcessEnvironment(ConfigFileApplicationListener.java:188)
    at org.springframework.boot.context.config.ConfigFileApplicationListener.onApplicationEnvironmentPreparedEvent(ConfigFileApplicationListener.java:175)
    at org.springframework.boot.context.config.ConfigFileApplicationListener.onApplicationEvent(ConfigFileApplicationListener.java:161)
    at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172)
    at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:165)
    at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139)
    at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:127)
    at org.springframework.boot.context.event.EventPublishingRunListener.environmentPrepared(EventPublishingRunListener.java:74)
    at org.springframework.boot.SpringApplicationRunListeners.environmentPrepared(SpringApplicationRunListeners.java:54)
    at org.springframework.boot.SpringApplication.prepareEnvironment(SpringApplication.java:351)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:317)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1246)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1234)
    at scratch.BindingApplication.main(BindingApplication.java:10)
Caused by: org.yaml.snakeyaml.scanner.ScannerException: mapping values are not allowed here
 in 'reader', line 3, column 16:
        max-history: 20
                   ^

    at org.yaml.snakeyaml.scanner.ScannerImpl.fetchValue(ScannerImpl.java:872)
    at org.yaml.snakeyaml.scanner.ScannerImpl.fetchMoreTokens(ScannerImpl.java:360)
    at org.yaml.snakeyaml.scanner.ScannerImpl.checkToken(ScannerImpl.java:226)
    at org.yaml.snakeyaml.parser.ParserImpl$ParseBlockMappingKey.produce(ParserImpl.java:557)
    at org.yaml.snakeyaml.parser.ParserImpl.peekEvent(ParserImpl.java:157)
    at org.yaml.snakeyaml.parser.ParserImpl.checkEvent(ParserImpl.java:147)
    at org.yaml.snakeyaml.composer.Composer.composeMappingNode(Composer.java:227)
    at org.yaml.snakeyaml.composer.Composer.composeNode(Composer.java:154)
    at org.yaml.snakeyaml.composer.Composer.composeValueNode(Composer.java:249)
    at org.yaml.snakeyaml.composer.Composer.composeMappingChildren(Composer.java:240)
    at org.yaml.snakeyaml.composer.Composer.composeMappingNode(Composer.java:228)
    at org.yaml.snakeyaml.composer.Composer.composeNode(Composer.java:154)
    at org.yaml.snakeyaml.composer.Composer.composeDocument(Composer.java:122)
    at org.yaml.snakeyaml.composer.Composer.getNode(Composer.java:84)
    at org.yaml.snakeyaml.constructor.BaseConstructor.getData(BaseConstructor.java:123)
    at org.yaml.snakeyaml.Yaml$1.next(Yaml.java:547)
    at org.springframework.beans.factory.config.YamlProcessor.process(YamlProcessor.java:161)
    at org.springframework.beans.factory.config.YamlProcessor.process(YamlProcessor.java:139)
    at org.springframework.boot.env.OriginTrackedYamlLoader.load(OriginTrackedYamlLoader.java:75)
    at org.springframework.boot.env.YamlPropertySourceLoader.load(YamlPropertySourceLoader.java:49)
    at org.springframework.boot.context.config.ConfigFileApplicationListener$Loader.load(ConfigFileApplicationListener.java:469)
    ... 18 common frames omitted

You can work around it by doing this:

logging.file: logs/app.log 
logging:
  file: 
    max-history: 20
    max-size: 50MB

All 17 comments

Thanks for getting in touch, but it feels like this is a question that would be better suited to Stack Overflow. As mentioned in the guidelines for contributing, we prefer to use GitHub issues only for bugs and enhancements. Feel free to update this issue with a link to the re-posted question (so that other people can find it) or add some more details if you feel this is a genuine bug.

yaml file can not support config for logging.file=xx.log with logging.file.max-history=20

@wilkinsona

in application.properties we can use this:

logging.file=logs/app.log
logging.file.max-history=20
logging.file.max-size=50MB

bug in application.yml can not set these three properties in same time

If you'd like us to spend some time investigating, you're going to have to spend some time to make sure that we're investigating the problem that you're seeing. As I already asked in #12522, please provide a minimal, complete, and verifiable example and we can re-open this issue and investigate.

I think I finally understand the issue here. A sample would have been very useful. As would have including the full stack trace.

Given the following YAML:

logging:
  file: logs/app.log
    max-history: 20
    max-size: 50MB

The following exception is raised:

java.lang.IllegalStateException: Failed to load property source from location 'classpath:/application.yml'
    at org.springframework.boot.context.config.ConfigFileApplicationListener$Loader.load(ConfigFileApplicationListener.java:481)
    at org.springframework.boot.context.config.ConfigFileApplicationListener$Loader.loadForFileExtension(ConfigFileApplicationListener.java:446)
    at org.springframework.boot.context.config.ConfigFileApplicationListener$Loader.load(ConfigFileApplicationListener.java:419)
    at org.springframework.boot.context.config.ConfigFileApplicationListener$Loader.load(ConfigFileApplicationListener.java:329)
    at org.springframework.boot.context.config.ConfigFileApplicationListener.addPropertySources(ConfigFileApplicationListener.java:205)
    at org.springframework.boot.context.config.ConfigFileApplicationListener.postProcessEnvironment(ConfigFileApplicationListener.java:188)
    at org.springframework.boot.context.config.ConfigFileApplicationListener.onApplicationEnvironmentPreparedEvent(ConfigFileApplicationListener.java:175)
    at org.springframework.boot.context.config.ConfigFileApplicationListener.onApplicationEvent(ConfigFileApplicationListener.java:161)
    at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172)
    at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:165)
    at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139)
    at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:127)
    at org.springframework.boot.context.event.EventPublishingRunListener.environmentPrepared(EventPublishingRunListener.java:74)
    at org.springframework.boot.SpringApplicationRunListeners.environmentPrepared(SpringApplicationRunListeners.java:54)
    at org.springframework.boot.SpringApplication.prepareEnvironment(SpringApplication.java:351)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:317)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1246)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1234)
    at scratch.BindingApplication.main(BindingApplication.java:10)
Caused by: org.yaml.snakeyaml.scanner.ScannerException: mapping values are not allowed here
 in 'reader', line 3, column 16:
        max-history: 20
                   ^

    at org.yaml.snakeyaml.scanner.ScannerImpl.fetchValue(ScannerImpl.java:872)
    at org.yaml.snakeyaml.scanner.ScannerImpl.fetchMoreTokens(ScannerImpl.java:360)
    at org.yaml.snakeyaml.scanner.ScannerImpl.checkToken(ScannerImpl.java:226)
    at org.yaml.snakeyaml.parser.ParserImpl$ParseBlockMappingKey.produce(ParserImpl.java:557)
    at org.yaml.snakeyaml.parser.ParserImpl.peekEvent(ParserImpl.java:157)
    at org.yaml.snakeyaml.parser.ParserImpl.checkEvent(ParserImpl.java:147)
    at org.yaml.snakeyaml.composer.Composer.composeMappingNode(Composer.java:227)
    at org.yaml.snakeyaml.composer.Composer.composeNode(Composer.java:154)
    at org.yaml.snakeyaml.composer.Composer.composeValueNode(Composer.java:249)
    at org.yaml.snakeyaml.composer.Composer.composeMappingChildren(Composer.java:240)
    at org.yaml.snakeyaml.composer.Composer.composeMappingNode(Composer.java:228)
    at org.yaml.snakeyaml.composer.Composer.composeNode(Composer.java:154)
    at org.yaml.snakeyaml.composer.Composer.composeDocument(Composer.java:122)
    at org.yaml.snakeyaml.composer.Composer.getNode(Composer.java:84)
    at org.yaml.snakeyaml.constructor.BaseConstructor.getData(BaseConstructor.java:123)
    at org.yaml.snakeyaml.Yaml$1.next(Yaml.java:547)
    at org.springframework.beans.factory.config.YamlProcessor.process(YamlProcessor.java:161)
    at org.springframework.beans.factory.config.YamlProcessor.process(YamlProcessor.java:139)
    at org.springframework.boot.env.OriginTrackedYamlLoader.load(OriginTrackedYamlLoader.java:75)
    at org.springframework.boot.env.YamlPropertySourceLoader.load(YamlPropertySourceLoader.java:49)
    at org.springframework.boot.context.config.ConfigFileApplicationListener$Loader.load(ConfigFileApplicationListener.java:469)
    ... 18 common frames omitted

You can work around it by doing this:

logging.file: logs/app.log 
logging:
  file: 
    max-history: 20
    max-size: 50MB

Since there's a workaround I don't think we should change things in 2.0. It is probably worth reviewing all properties in 2.1 to make sure things map to yaml nicely.

It's not the file that's the issue. It's the dash (-) in both max-history and max-size. They would work fine if they were maxHistory and maxSize.

Here is the error:

mapping values are not allowed here in 'reader', line 3, column 16:
max-history: 20

That's why the suggested workaround actually doesn't work.
It thinks that it's trying to map some values to a list like:

teams:
  red:
  - Player1
  - Player2
  blue:
  - Player3

..but it's giving the error because it's seeing the dash and trying to build a list.

Since I can't specify those values I'm spewing endless logs like crazy.

@shutterstein I'm not following how the snippet of yaml you've pasted related to the issue above. My understanding is that a entry that starts with - is treated as a list, but a entry with a - in the middle is fine. I tried the work-around locally and it was fine. Can you share a sample that fails?

@philwebb The error specified above (which I was also experiencing) was coming from whichever of the max-history or max-size were specified first. I just gave the example because it seemed from the error message that it was thinking we were trying to specify a list or something along those lines.

As far as an example, I had virtually copied how you had done it and it still failed with the same error about "mapping values" and pointed at the "max-history" attribute (which was specified first in my yml file). Here was how I had it when it was still failing with the same error on max-history:

logging.file: logs/server.log
logging:
  level:
    root: info
  file:
    max-history: 10
    max-size: 100MB

I did, however, get it to workaround properly for those two properties by specifying it similarly as you had done in the file specification:

logging.file.max-history: 10
logging.file.max-size: 100MB

So that has helped me workaround the issue now. Thanks!

@philwebb Honestly, there aren't that much properties that have this issue. Wouldn't it be a better idea to just consider renaming logging.file to logging.file.name, in which case, the resulting configuration would be:

application.properties

logging.file.name=server.log
logging.file.max-history=10
logging.file.max-size=100MB

application.yml

logging:
  file:
    name: server.log
    max-history: 10
    max-size: 100MB

See LogFile.java#L42

Other than logging.file, the only other property that is affected by this would be logging.level, like so:

logging:
  level:
    org: 
      springframework: ERROR    <---- oops
         orm: INFO

but I personally think that it's not as easy to read as something like this:

logging:
  level:
    org.springframework: ERROR
    org.springframework.orm: INFO

I think that the logging.level issue is understandable, but I don't think that a change should be made to support having a structure element (e.g. file) support both a value and a substructure.

YAML is a superset of JSON, and with JSON, this logging.file case would translate to trying to do the following:

{
  "logging": {
    "file": "server.log" {   <---- a value and an array? that doesn't make sense
      "max-history": "10",
       "max-size": "100MB"
    }
  }
}

Clearly JSON doesn't support that, instead, the configuration would be expected to be like so:

{
  "logging": {
    "file": {   
      "name": "server.log",
      "max-history": "10",
       "max-size": "100MB"
    }
  }
}

Just my 2 cents

@TwinProduction Indeed, that's what I had in mind but this one slipped though the net for 2.1. I've assigned it to 2.2.x so that we get to it next time.

Hi, the comparison with json is a good point, even if it seems that yaml does allow it.
If you do decide to make that syntax illegal, may I advise to remove its use within spring then :

spring:
  profiles: currentProfile
  profiles.include: profileToInclude

Related: https://stackoverflow.com/questions/35372340/spring-profile-include-issue-with-yaml-file/53657492

@Chatom YAML is a superset of JSON, therefore there's nothing wrong with supporting some of its syntax.

As mentioned above, there's a need for supporting that syntax, here's an example (same example I gave above, but a bit more explicit):

logging:
  level:
    org.springframework: INFO
    org.springframework.orm: INFO

If we do make the syntax completely illegal, then we'd have this:

logging:
  level:
    org:
      springframework: INFO
        orm: WARN        <--- Syntax error

Because it would translate to something like this, which is invalid:

{
  "logging": {
    "level": {
      "org": {
        "springframework": "INFO" {   <---- Syntax error
          "orm": "WARN"
        }
      }
    }
  } 
}

TL;DR: 1 key can only be assigned to 1 value, and some properties must have more than one value (e.g. in this case, JSON object and a String)


Honestly, this wouldn't be an issue if IDEs gave a syntax error for YAML when it should, but currently, most IDEs don't complain about things like this:

foo: bar
  bar: foo

It should seriously be considered as a parsing error, because it breaks backward compatibility with JSON.

I think I finally understand the issue here. A sample would have been very useful. As would have including the full stack trace.

Given the following YAML:

logging:
  file: logs/app.log
    max-history: 20
    max-size: 50MB

The following exception is raised:

java.lang.IllegalStateException: Failed to load property source from location 'classpath:/application.yml'
  at org.springframework.boot.context.config.ConfigFileApplicationListener$Loader.load(ConfigFileApplicationListener.java:481)
  at org.springframework.boot.context.config.ConfigFileApplicationListener$Loader.loadForFileExtension(ConfigFileApplicationListener.java:446)
  at org.springframework.boot.context.config.ConfigFileApplicationListener$Loader.load(ConfigFileApplicationListener.java:419)
  at org.springframework.boot.context.config.ConfigFileApplicationListener$Loader.load(ConfigFileApplicationListener.java:329)
  at org.springframework.boot.context.config.ConfigFileApplicationListener.addPropertySources(ConfigFileApplicationListener.java:205)
  at org.springframework.boot.context.config.ConfigFileApplicationListener.postProcessEnvironment(ConfigFileApplicationListener.java:188)
  at org.springframework.boot.context.config.ConfigFileApplicationListener.onApplicationEnvironmentPreparedEvent(ConfigFileApplicationListener.java:175)
  at org.springframework.boot.context.config.ConfigFileApplicationListener.onApplicationEvent(ConfigFileApplicationListener.java:161)
  at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172)
  at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:165)
  at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139)
  at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:127)
  at org.springframework.boot.context.event.EventPublishingRunListener.environmentPrepared(EventPublishingRunListener.java:74)
  at org.springframework.boot.SpringApplicationRunListeners.environmentPrepared(SpringApplicationRunListeners.java:54)
  at org.springframework.boot.SpringApplication.prepareEnvironment(SpringApplication.java:351)
  at org.springframework.boot.SpringApplication.run(SpringApplication.java:317)
  at org.springframework.boot.SpringApplication.run(SpringApplication.java:1246)
  at org.springframework.boot.SpringApplication.run(SpringApplication.java:1234)
  at scratch.BindingApplication.main(BindingApplication.java:10)
Caused by: org.yaml.snakeyaml.scanner.ScannerException: mapping values are not allowed here
 in 'reader', line 3, column 16:
        max-history: 20
                   ^

  at org.yaml.snakeyaml.scanner.ScannerImpl.fetchValue(ScannerImpl.java:872)
  at org.yaml.snakeyaml.scanner.ScannerImpl.fetchMoreTokens(ScannerImpl.java:360)
  at org.yaml.snakeyaml.scanner.ScannerImpl.checkToken(ScannerImpl.java:226)
  at org.yaml.snakeyaml.parser.ParserImpl$ParseBlockMappingKey.produce(ParserImpl.java:557)
  at org.yaml.snakeyaml.parser.ParserImpl.peekEvent(ParserImpl.java:157)
  at org.yaml.snakeyaml.parser.ParserImpl.checkEvent(ParserImpl.java:147)
  at org.yaml.snakeyaml.composer.Composer.composeMappingNode(Composer.java:227)
  at org.yaml.snakeyaml.composer.Composer.composeNode(Composer.java:154)
  at org.yaml.snakeyaml.composer.Composer.composeValueNode(Composer.java:249)
  at org.yaml.snakeyaml.composer.Composer.composeMappingChildren(Composer.java:240)
  at org.yaml.snakeyaml.composer.Composer.composeMappingNode(Composer.java:228)
  at org.yaml.snakeyaml.composer.Composer.composeNode(Composer.java:154)
  at org.yaml.snakeyaml.composer.Composer.composeDocument(Composer.java:122)
  at org.yaml.snakeyaml.composer.Composer.getNode(Composer.java:84)
  at org.yaml.snakeyaml.constructor.BaseConstructor.getData(BaseConstructor.java:123)
  at org.yaml.snakeyaml.Yaml$1.next(Yaml.java:547)
  at org.springframework.beans.factory.config.YamlProcessor.process(YamlProcessor.java:161)
  at org.springframework.beans.factory.config.YamlProcessor.process(YamlProcessor.java:139)
  at org.springframework.boot.env.OriginTrackedYamlLoader.load(OriginTrackedYamlLoader.java:75)
  at org.springframework.boot.env.YamlPropertySourceLoader.load(YamlPropertySourceLoader.java:49)
  at org.springframework.boot.context.config.ConfigFileApplicationListener$Loader.load(ConfigFileApplicationListener.java:469)
  ... 18 common frames omitted

You can work around it by doing this:

logging.file: logs/app.log 
logging:
  file: 
    max-history: 20
    max-size: 50MB

No exception but no effect for springboot 1.5.6

When using the following YAML:

logging:
  file: logs/app.log
    max-history: 20
    max-size: 50MB

It fails as described above with 1.5.6.

When using the following YAML:

logging.file: logs/app.log 
logging:
  max-history: 20
  max-size: 50MB

The configuration takes effect and logs are written to logs/app.log as expected.

@controlloli If you need any further guidance on how to use the workaround, please come and chat on Gitter or ask a question on Stack Overflow.

I couldn't find any other properties that can't be mapped in YAML. spring.profiles when combined with spring.profiles.include/active seems to be the only one and I don't think we should do anything about that till we decide how we want to handle include profiles in profile-specific YAML documents.

I'm in favor of closing this issue now that logging.file has been renamed to logging.file.name.

Was this page helpful?
0 / 5 - 0 ratings