Spring Boot Actuator provides several endpoints to monitor an application as:
/metrics
/beans
/health
...
Checking the endpoints with:
curl http://localhost:8080/metrics
results in:
{"counter.status.200.env":1,"counter.status.200.health":1,"counter.status.200.info":2,"counter.status.200.metrics":2,"gauge.response.env":5.0,"gauge.response.health":22.0,"gauge.response.info":1.0,"gauge.response.metrics":1.0,"mem":1030144,"mem.free":56118,"processors":8,"uptime":5108095,"instance.uptime":5102906,"heap.committed":1030144,"heap.init":262144,"heap.used":974031,"heap":3728384,"threads.peak":81,"threads.daemon":21,"threads":77,"classes":8854,"classes.loaded":8860,"classes.unloaded":6,"gc.ps_scavenge.count":119,"gc.ps_scavenge.time":7223,"gc.ps_marksweep.count":12,"gc.ps_marksweep.time":17573}
This is fine for machine consumption but hard to read by humans.
I'd like to format (i.e. pretty print) the JSON output of the Spring Boot Actuator endpoints to make them easier to read by operations personel.
Something like:
{
"counter.status.200.env":1,
"counter.status.200.health":1,
"counter.status.200.info":2,
"counter.status.200.metrics":2,
"gauge.response.env":5.0,
"gauge.response.health":22.0,
"gauge.response.info":1.0,
...
}
I tried setting
http.mappers.json-pretty-print=true
but this setting didn't affect the Actuator output.
Is there a configuration to enable pretty print of the Spring Boot Actuator JSON output?
UPDATE:
The official sample works for me.
It's important to follow the comments from #DaveSyer: the property to set is
http.mappers.jsonPrettyPrint=true
Investigation is still under way.
In the meantime I use the the json pretty print command line as workaround:
Install jsonpp (e.g. for OS X):
brew install jsonpp
Then pipe the curl output trough jsonpp which formats the json file on the fly:
curl http://localhost:8080/metrics | jsonpp
Results in:
{
"counter.status.200.env": 1,
"counter.status.200.health": 1,
"counter.status.200.info": 2,
"counter.status.200.metrics": 2,
...
}
As per http://docs.spring.io/spring-boot/docs/current/reference/html/howto-spring-mvc.html#howto-customize-the-jackson-objectmapper, the official way to enable pretty print with Jackson in Spring Boot (1.2.2 at least) is to set the following property:
# Pretty-print JSON responses
spring.jackson.serialization.indent_output=true
For Spring Boot 1.5.1 I have in my YML file:
spring:
jackson:
serialization:
INDENT_OUTPUT: true
#BertrandRenuart answer was the closest, but by IDE did not see indent_output as correct.
The "http.mappers" property works for me but I think you might need it camel cased ("jsonPrettyPrint").
Do the following:
#Configuration
public class JacksonConfig {
#Autowired
private ObjectMapper objectMapper; //reuse the pre-configured mapper
#PostConstruct
public void setup() {
objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
//whatever else you need
}
}
This works because Spring Boot uses an ObjectMapper bean to perform all the JSON related operations.
Note however that this configuration will pretty print all JSON outputs, not just the actuator related stuff.
UPDATE
The answer from #DaveSyer is obviously better! I hadn't found the HttpMapperProperties object which is used to configure Jackson. This is it's Javadoc
Actually I wanted to do the same. But then I asked: why? To debug my service better which comes with a small performance penalty.
Just use a browser extension, like this one :) to get a view like this one
With spring-boot 1.2.6, you need to use:
spring.jackson.serialization.INDENT_OUTPUT=true
From my log when using the old http.mappers.*:
http.mappers.json-pretty-print is deprecated. If you are using Jackson, spring.jackson.serialization.INDENT_OUTPUT=true should be used instead.
I use Python's commonly installed json.tool module:
curl --silent http://localhost:8080/metrics | python -mjson.tool
If you're using gson serialization with Spring, then none of the other answers will work for you. You'll have to use this configuration option:
spring.gson.pretty-printing=true
Confirmed working with Spring Boot as of version 2.0.3.Release.
I use jq for pretty printing JSON as well as filtering it. It's basically sed for JSON. On the mac, it can be installed with homebrew. (https://stedolan.github.io/jq/)
curl http://localhost:8080/metrics | jq
Instead of using curl I like to use httpie as a http command line client:
http http://localhost:8080/metrics
This would already format and syntax highlight the json response without having to pipe the output into another command. Also the command syntax is a bit more human friendly.
Spring Boot Actuator uses its own isolated ObjectMapper instance by default in which indent-output is disabled. To enable pretty-print of actuator output, you must set the following properties:
spring.jackson.serialization.indent-output=true
management.endpoints.jackson.isolated-object-mapper=false
Unfortunately, the application property
spring.jackson.serialization.INDENT_OUTPUT
did not work for me (spring boot versions 1.2.6 to 1.4.0.RELEASE). Instead, in my extension of WebMvcConfigurerAdapter, I've overridden configureMessageConverters() and added my own Jackson2ObjectMapperBuilder:
#Configuration
#EnableWebMvc
public class WebMvcConfig extends WebMvcConfigurerAdapter {
...
private MappingJackson2HttpMessageConverter jacksonMessageConverter() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder()
.featuresToDisable(SerializationFeature.FAIL_ON_EMPTY_BEANS,
SerializationFeature.WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS)
.featuresToEnable(SerializationFeature.INDENT_OUTPUT).modulesToInstall(hibernate4Module());
// can use this instead of featuresToEnable(...)
builder.indentOutput(true);
return new MappingJackson2HttpMessageConverter(builder.build());
}
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(jacksonMessageConverter());
super.configureMessageConverters(converters);
}
...
}
That seem to do the trick for me on Spring boot 1.4.0.RELEASE and my actuator output is now pretty printed (along with every other json output)
Here is my Emacs function to retrieve Spring Actuator Json from endpoints:
(defvar my/spring-actuator-server-history nil)
(defvar my/spring-actuator-last-server "http://localhost:8080")
(defvar my/spring-actuator-path-history nil)
(defvar my/spring-actuator-path-completion
'("actuator" "auditevents" "autoconfig" "beans" "configprops" "dump" "env" "flyway" "health" "heapdump"
"info" "jolokia" "liquibase" "logfile" "loggers" "mappings" "metrics" "shutdown" "trace")))
(defun my/spring-actuator (server path)
(interactive (list (read-string "Server: " my/spring-actuator-last-server 'my/spring-actuator-server-history)
(completing-read "Path: " my/spring-actuator-path-completion nil nil "" 'my/spring-actuator-path-history)))
(setq my/spring-actuator-last-server server)
(let ( (bufname (format "actuator: %s" path)) )
(when (get-buffer bufname)
(kill-buffer bufname))
(switch-to-buffer (url-retrieve-synchronously (format "%s/%s" server path)))
(rename-buffer bufname)
(goto-char (point-min))
(re-search-forward "^$" nil 'move)
(forward-char)
(delete-region (point-min) (point))
(json-pretty-print-buffer)
(json-mode) ))
If you don't like dependency on external json-mode library replace it with js-mode.
In case somebody with Spring Boot 2 (2.1.1 in my case) stumbles over this question as I did: We faced the same problem, and none of the answers helped for 2.1.1.
So what we did was to replace the existing endpoint (health in our case) with a new one. I described it at the end of this answer. And yes, this limits our solution to this single endpoint, but on the other hand it has the advantage of being able to format the output in any way you want - including pretty print JSON, but also output styled HTML if requested (by a service technician in a browser in our case). Note the produces attribute of #ReadOperation to achieve that.
This is the latest as of 12/27/2022. The below works.
-For application.yml config.
spring:
jackson:
serialization:
indent-output: true
But the above config won't work, if you implement WebMvcConfigurer class. In that case, override like below to make it work.
#Override
public void extendMessageConverters( List<HttpMessageConverter<?>> converters ) {
for ( HttpMessageConverter<?> converter : converters ) {
if ( converter instanceof MappingJackson2HttpMessageConverter) {
MappingJackson2HttpMessageConverter jacksonConverter = (MappingJackson2HttpMessageConverter) converter;
jacksonConverter.setPrettyPrint( true );
}
}
}
this not working
spring.jackson.serialization.INDENT_OUTPUT=true
this is working spring.jackson.serialization.indent-output=true
Related
I'm very new to Scala and I'm trying to send a json from a client to a service.
I have an interface ("trait") where I have the method receiveData takes a JSONObject as an argument, but apparently it's deprecated.
trait DataService {
def receiveData(data: JSONObject, clientId: String): Unit
}
I have this Scala version ThisBuild / scalaVersion := "3.0.0-RC1"
I have looked around for what I should use instead and found nothing.
it says
#deprecated("Use The Scala Library Index to find alternatives: https://index.scala-lang.org/", "1.0.6")
but I don't know how to search for things there.
Appreciate all help!
EDIT:
ANSWER: there are plenty: Play-Json, zio-json, Circe, Jackson...
Dependencies used:
implementation("org.jooq:jooq:3.17.2")
implementation("org.jooq:jooq-meta:3.17.2")
implementation("org.jooq:jooq-kotlin-coroutines:3.17.2")
implementation("org.jooq:jooq-kotlin:3.17.2")
runtimeOnly("io.r2dbc:r2dbc-spi") {
version { strictly("0.9.1.RELEASE") }
}
runtimeOnly("dev.miku:r2dbc-mysql:0.8.2.RELEASE")
runtimeOnly("io.r2dbc:r2dbc-pool:0.9.1.RELEASE")
// Kotlin 1.6.20
implementation("io.projectreactor.kotlin:reactor-kotlin-extensions")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-jdk8")
Using R2DBC DSL configuration:
private val dsl = DSL.using(
ConnectionFactories.get(
ConnectionFactoryOptions
.parse("r2dbc:pool:mysql://localhost:3303/db")
.mutate()
.option(ConnectionFactoryOptions.USER, databaseUser)
.option(ConnectionFactoryOptions.PASSWORD, databasePassword)
.build()
)
)
Running on JDK17:
DSL.using(dsl.configuration()).insertInto(table)
.set(fromDomain(item)).apply(builder)
.executeAsync()
.await()
throws an error on line with .await()
Attempt to execute a blocking method (e.g. Query.execute() or ResultQuery.fetch()) when only an R2BDC ConnectionFactory was configured. jOOQ's RowCountQuery and ResultQuery extend Publisher, which allows for reactive streams implementations to subscribe to the results of a jOOQ query. Simply embed your query in the stream, e.g. using Flux.from(query). See also: https://www.jooq.org/doc/latest/manual/sql-execution/fetching/reactive-fetching/
org.jooq.exception.DetachedException: Attempt to execute a blocking method (e.g. Query.execute() or ResultQuery.fetch()) when only an R2BDC ConnectionFactory was configured. jOOQ's RowCountQuery and ResultQuery extend Publisher, which allows for reactive streams implementations to subscribe to the results of a jOOQ query. Simply embed your query in the stream, e.g. using Flux.from(query). See also: https://www.jooq.org/doc/latest/manual/sql-execution/fetching/reactive-fetching/
(Coroutine boundary)
at ...
Caused by: org.jooq.exception.DetachedException: Attempt to execute a blocking method (e.g. Query.execute() or ResultQuery.fetch()) when only an R2BDC ConnectionFactory was configured. jOOQ's RowCountQuery and ResultQuery extend Publisher, which allows for reactive streams implementations to subscribe to the results of a jOOQ query. Simply embed your query in the stream, e.g. using Flux.from(query). See also: https://www.jooq.org/doc/latest/manual/sql-execution/fetching/reactive-fetching/
at org.jooq_3.17.2.MYSQL.debug(Unknown Source)
at app//org.jooq.impl.AbstractQuery.execute(AbstractQuery.java:300)
at app//org.jooq.impl.Tools$3$1.block(Tools.java:5803)
at java.base#17.0.1/java.util.concurrent.ForkJoinPool.compensatedBlock(ForkJoinPool.java:3449)
at java.base#17.0.1/java.util.concurrent.ForkJoinPool.managedBlock(ForkJoinPool.java:3432)
at app//org.jooq.impl.Tools$3.get(Tools.java:5800)
at java.base#17.0.1/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1768)
at java.base#17.0.1/java.util.concurrent.CompletableFuture$AsyncSupply.exec(CompletableFuture.java:1760)
at java.base#17.0.1/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:373)
at java.base#17.0.1/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1182)
at java.base#17.0.1/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1655)
at java.base#17.0.1/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1622)
at java.base#17.0.1/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:165)
although executeAsync is used instead of execute. I would not expect this error and would expect the query to run successfully. Thanks for any help in trying to solve this issue.
I believe I understand it now. Instead of .executeAsync().await() I can use .awaitLast() and instead of fetchAsync() I can use .asFlow().toList()
There's a known limitation (as of jOOQ 3.17) that jOOQ doesn't execute fetchAsync() and other CompletionStage producing API calls via R2DBC yet, see: https://github.com/jOOQ/jOOQ/issues/13590
But since you're using kotlin coroutines as a "frontend" of jOOQ reactive queries, just use the recommended bridge module:
<dependency>
<groupId>org.jooq</groupId>
<artifactId>jooq-kotlin-coroutines</artifactId>
</dependency>
It allows you to use jOOQ like this, for example:
fun coroutinesWithTransactions() {
val actor: ActorRecord = runBlocking {
insertActorTransaction()
}
println(actor)
}
suspend fun insertActorTransaction(): ActorRecord {
return ctx.transactionCoroutine(::insertActor)
}
suspend fun insertActor(c: Configuration): ActorRecord = c.dsl()
.insertInto(ACTOR)
.columns(ACTOR.ACTOR_ID, ACTOR.FIRST_NAME, ACTOR.LAST_NAME)
.values(201L, "A", "A")
.returning()
.awaitFirst()
See also:
https://blog.jooq.org/3-17-0-release-with-computed-columns-audit-columns-pattern-matching-reactive-transactions-and-kotlin-coroutine-support/
I have some model definition inside a XSD file and I need to reference these models from an OpenApi definition. Manually remodeling is no option since the file is too large, and I need to put it into a build system, so that if the XSD is changed, I can regenerate the models/schemas for OpenApi.
What I tried and what nearly worked is using xsd2json and then converting it with the node module json-schema-to-openapi. However xsd2json is dropping some of the complexElement models. For example "$ref": "#/definitions/tns:ContentNode" is used inside of one model as the child type but there is no definition for ContentNode in the schema, where when I look into the XSD, there is a complexElement definition for ContentNode.
Another approach which I haven't tried yet but seems a bit excessive to me is using xjb to generate Java models from the XSD and then using JacksonSchema to generate the json schema.
Is there any established library or way, to use XSD in OpenApi?
I ended up implementing the second approach using jaxb to convert the XSD to java models and then using Jackson to write the schemas to files.
Gradle:
plugins {
id 'java'
id 'application'
}
group 'foo'
version '1.0-SNAPSHOT'
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
testCompile group: 'junit', name: 'junit', version: '4.12'
compile group: 'com.fasterxml.jackson.module', name: 'jackson-module-jsonSchema', version: '2.9.8'
}
configurations {
jaxb
}
dependencies {
jaxb (
'com.sun.xml.bind:jaxb-xjc:2.2.7',
'com.sun.xml.bind:jaxb-impl:2.2.7'
)
}
application {
mainClassName = 'foo.bar.Main'
}
task runConverter(type: JavaExec, group: 'application') {
classpath = sourceSets.main.runtimeClasspath
main = 'foo.bar.Main'
}
task jaxb {
System.setProperty('javax.xml.accessExternalSchema', 'all')
def jaxbTargetDir = file("src/main/java")
doLast {
jaxbTargetDir.mkdirs()
ant.taskdef(
name: 'xjc',
classname: 'com.sun.tools.xjc.XJCTask',
classpath: configurations.jaxb.asPath
)
ant.jaxbTargetDir = jaxbTargetDir
ant.xjc(
destdir: '${jaxbTargetDir}',
package: 'foo.bar.model',
schema: 'src/main/resources/crs.xsd'
)
}
}
compileJava.dependsOn jaxb
With a converter main class, that does something along the lines of:
package foo.bar;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.module.jsonSchema.JsonSchema;
import com.fasterxml.jackson.module.jsonSchema.JsonSchemaGenerator;
import foo.bar.model.Documents;
public class Main {
public static void main(String[] args) {
ObjectMapper mapper = new ObjectMapper();
JsonSchemaGenerator schemaGen = new JsonSchemaGenerator(mapper);
try {
JsonSchema schema = schemaGen.generateSchema(Documents.class);
System.out.print(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(schema));
} catch (JsonMappingException e) {
e.printStackTrace();
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}
}
It is still not perfect though,... this would need to iterate over all the model classes and generate a file with the schema. Also it doesn't use references, if a class has a member of another class, the schema is printed inline instead of referencing. This requires a bit more customization with the SchemaFactoryWrapper but can be done.
The problem you have is that you are applying inference tooling over a multi-step conversion. As you have found, inference tooling is inherently fussy and will not work in all situations. It's kind of like playing Chinese whispers - every step of the chain is potentially lossy, so what you get out the other end may be garbled.
Based on the alternative approach you suggest, I would suggest a similar solution:
OpenAPI is, rather obviously, an API definition standard. It should be possible for you to take a code first approach, composing your API operations in code and exposing the types generated from XJB. Then you can use Apiee and its annotations to generate the OpenAPI definition. This assumes you are using JAX-RS for your API.
This is still a two-step process, but one with a higher chance of success. The benefit here is that your first step, inferring your XSD types into java types, will hopefully have very little (if any) impact on the code which defines your API operations. Although there will still be a manual step (updating the models) the OpenAPI definition will update automatically once the code has been rebuilt.
I have a problem with searching by Elasticsearch. I use JHipser generator v.3.4.0 and I initialize my H2 database by *.csv files. Next I try search something, Elasticsearch always returns [] unless I add some object manually then I get only this one object. Do you have any ideas how to fix it?
Ok. I used jhipster-elasticsearch-reindexer and did POST form swagger-ui and now all objects can be reached by elasticsearch.
Edit:
I automatized Elasticsearch Reindexing. It invokes after start application. If you want to do it, follow the steps below.
Steps:
1. Add to your JHipster project Elasticsearch Reindexer from: https://github.com/geraldhumphries/generator-jhipster-elasticsearch-reindexer
2. Reindex elasticsearch on start by adding class:
#Component
public class ElasticsearchReindexOnStart implements ApplicationListener<ContextRefreshedEvent> {
private final ElasticsearchIndexService elasticsearchIndexService;
#Inject
public ElasticsearchReindexOnStart(ElasticsearchIndexService elasticsearchIndexService) {
this.elasticsearchIndexService = elasticsearchIndexService;
}
#Override
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
elasticsearchIndexService.reindexAll();
}
}
After that, restart your application and now it should works without doing POST from swagger-ui.
Does anyone know if Spring has any extensions that allow for configuring its ApplicationContext via JSON (or really any other format) rather than XML? I couldn't find anything in the official docs, but I was wondering if there were any other open source extensions that could allow this.
Just to be clear, I'm not talking about configuring SpringMVC to set up a RESTful JSON-based web service or anything like that, just if it's possible to do Spring app configuration via JSON instead of XML.
As far as I know there is no project to support JSON as configuration source. It should be relatively easy to kick-start, (Spring container has no dependency on XML, it is just a way to construct bean definitions). However it is much more work than you might think.
Note that Spring provides xml-schema to assist you in writing correct XML. You won't get that much in JSON. Also many DSLs were built on top of Spring XML and custom namespaces support (spring-integration, mule-esb and others use it).
If you hate XML (many do), try out Java Configuration, available since 3.0 and improved in 3.1:
#Configuration
public class MyBeans {
#Bean
public Foo foo() {
return new Foo();
}
#Bean
public Bar bar() {
return new Bar(foo());
}
#Bean
public Buzz buzz() {
Buzz buzz = new Buzz();
buzz.setFoo(foo());
return buzz;
}
}
Interesting fact: thanks to some fancy proxying, foo() is called exactly once here, even though referenced twice.
Try JSConf library available on maven central, it's support Properties, HOCON and JSON format.
You can inject values from external file to your service and more !
Sample usage of JavaConfig :
You data stored on file app.conf
{
"root":{
"simpleConf":{
"url":"Hello World",
"port":12,
"aMap":{
"key1":"value1",
"key2":"value2"
},
"aList":[
"value1",
"value2"
]
}}
You service where your configuration must be inject
#Service("service")
public class Service {
#Autowired
private ConfigBean configBean;
}
Declare a interface to access your configuration values from your service
#ConfigurationProperties("root/simpleConf")
public interface ConfigBean {
String getUrl();
int getPort();
Map getAMap();
List getAList();
}
And your Spring configuration bean :
#Configuration
public class ContextConfiguration {
#Bean
public static ConfigurationFactory configurationFactory() {
return new ConfigurationFactory().withResourceName("app.conf") //
.withScanPackage("org.jsconf.core.sample.bean");
}
}