From 6a6067e70b16c4c2b7bbc0fc1b552a82a0caf7d0 Mon Sep 17 00:00:00 2001 From: "Zimin A.N." Date: Sun, 3 May 2026 15:59:40 +0300 Subject: [PATCH] feat(projection-writer): Kafka consumer + Redis materialization per-locale MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ordinis-projection-writer полностью реализован, last module: - OrdinisProjectionWriterApplication: Spring Boot main + JpaRepositories - RedisKeys: convention 'ord::::' для flat плюс ':_raw' для multi-locale, плюс 'ord:idx::' set index - ProjectionWriter: для каждой supported locale справочника flatten data и записать в Redis. Также пишет _raw multi-locale ключ. На delete стирает все варианты + удаляет из index. Метрики ordinis_projection_records_* - RecordEventListener: @KafkaListener на 3 scope-топика, group ordinis-projection-cuod. Парсит EventEnvelope, разбирает payload.type (RecordCreated/Updated/Deleted), вызывает upsert или delete. - application.yml profile-based: dev (port-forward), k8s (Vault role ordinis-projection-writer) - logback-spring.xml как у других сервисов ordinis-events-api: добавлен явный 'type' accessor в каждом record (@JsonProperty type). Без него Jackson не сериализовал discriminator из @JsonTypeInfo (при generic T payload Jackson не знает declared type). ordinis-infra: Redis StatefulSet (redis:7.4-alpine, AOF, maxmemory 256mb LRU, secret password) в namespace ordinis-dev на :6379. E2E (3 сервиса параллельно): POST /spacecraft/records {AQUA-1, name:{ru-RU:'Аква',en-US:'Aqua'}, ...} → outbox row id=7 published_at=true → ordinis_outbox_published_total +1 → projection log: 'Projection upserted: spacecraft:AQUA-1 (2 locales)' → Redis ord:cuod:spacecraft:AQUA-1:ru-RU = {name:'Аква',...} → Redis ord:cuod:spacecraft:AQUA-1:en-US = {name:'Aqua',...} → Redis ord:cuod:spacecraft:AQUA-1:_raw = {name:{ru-RU,en-US},...} → Redis ord:idx:cuod:spacecraft set member 'AQUA-1' 10 of 10 модулей реализованы. Last module done. --- .../DictionaryDefinitionCreated.java | 6 +- .../DictionaryDefinitionUpdated.java | 6 +- .../record/DictionaryRecordCreated.java | 6 +- .../record/DictionaryRecordDeleted.java | 6 +- .../record/DictionaryRecordUpdated.java | 6 +- ordinis-projection-writer/pom.xml | 21 +++ .../OrdinisProjectionWriterApplication.java | 16 +++ .../ordinis/projection/ProjectionWriter.java | 122 ++++++++++++++++++ .../projection/RecordEventListener.java | 89 +++++++++++++ .../ordinis/projection/RedisKeys.java | 40 ++++++ .../src/main/resources/application.yml | 121 +++++++++++++++++ .../src/main/resources/logback-spring.xml | 35 +++++ 12 files changed, 469 insertions(+), 5 deletions(-) create mode 100644 ordinis-projection-writer/src/main/java/cloud/nstart/terravault/ordinis/projection/OrdinisProjectionWriterApplication.java create mode 100644 ordinis-projection-writer/src/main/java/cloud/nstart/terravault/ordinis/projection/ProjectionWriter.java create mode 100644 ordinis-projection-writer/src/main/java/cloud/nstart/terravault/ordinis/projection/RecordEventListener.java create mode 100644 ordinis-projection-writer/src/main/java/cloud/nstart/terravault/ordinis/projection/RedisKeys.java create mode 100644 ordinis-projection-writer/src/main/resources/application.yml create mode 100644 ordinis-projection-writer/src/main/resources/logback-spring.xml diff --git a/ordinis-events-api/src/main/java/cloud/nstart/terravault/ordinis/events/definition/DictionaryDefinitionCreated.java b/ordinis-events-api/src/main/java/cloud/nstart/terravault/ordinis/events/definition/DictionaryDefinitionCreated.java index fd505f0..532acdf 100644 --- a/ordinis-events-api/src/main/java/cloud/nstart/terravault/ordinis/events/definition/DictionaryDefinitionCreated.java +++ b/ordinis-events-api/src/main/java/cloud/nstart/terravault/ordinis/events/definition/DictionaryDefinitionCreated.java @@ -21,4 +21,8 @@ public record DictionaryDefinitionCreated( List supportedLocales, String defaultLocale, OffsetDateTime createdAt, - String createdBy) implements DictionaryDefinitionEvent {} + String createdBy) implements DictionaryDefinitionEvent { + + @com.fasterxml.jackson.annotation.JsonProperty("type") + public String type() { return "DefinitionCreated"; } +} diff --git a/ordinis-events-api/src/main/java/cloud/nstart/terravault/ordinis/events/definition/DictionaryDefinitionUpdated.java b/ordinis-events-api/src/main/java/cloud/nstart/terravault/ordinis/events/definition/DictionaryDefinitionUpdated.java index 49506ba..49780e7 100644 --- a/ordinis-events-api/src/main/java/cloud/nstart/terravault/ordinis/events/definition/DictionaryDefinitionUpdated.java +++ b/ordinis-events-api/src/main/java/cloud/nstart/terravault/ordinis/events/definition/DictionaryDefinitionUpdated.java @@ -26,4 +26,8 @@ public record DictionaryDefinitionUpdated( List supportedLocales, String defaultLocale, OffsetDateTime updatedAt, - String updatedBy) implements DictionaryDefinitionEvent {} + String updatedBy) implements DictionaryDefinitionEvent { + + @com.fasterxml.jackson.annotation.JsonProperty("type") + public String type() { return "DefinitionUpdated"; } +} diff --git a/ordinis-events-api/src/main/java/cloud/nstart/terravault/ordinis/events/record/DictionaryRecordCreated.java b/ordinis-events-api/src/main/java/cloud/nstart/terravault/ordinis/events/record/DictionaryRecordCreated.java index 92e46b6..b73d839 100644 --- a/ordinis-events-api/src/main/java/cloud/nstart/terravault/ordinis/events/record/DictionaryRecordCreated.java +++ b/ordinis-events-api/src/main/java/cloud/nstart/terravault/ordinis/events/record/DictionaryRecordCreated.java @@ -22,4 +22,8 @@ public record DictionaryRecordCreated( OffsetDateTime validFrom, OffsetDateTime validTo, OffsetDateTime createdAt, - String createdBy) implements DictionaryRecordEvent {} + String createdBy) implements DictionaryRecordEvent { + + @com.fasterxml.jackson.annotation.JsonProperty("type") + public String type() { return "RecordCreated"; } +} diff --git a/ordinis-events-api/src/main/java/cloud/nstart/terravault/ordinis/events/record/DictionaryRecordDeleted.java b/ordinis-events-api/src/main/java/cloud/nstart/terravault/ordinis/events/record/DictionaryRecordDeleted.java index 1702e07..87eae9f 100644 --- a/ordinis-events-api/src/main/java/cloud/nstart/terravault/ordinis/events/record/DictionaryRecordDeleted.java +++ b/ordinis-events-api/src/main/java/cloud/nstart/terravault/ordinis/events/record/DictionaryRecordDeleted.java @@ -16,4 +16,8 @@ public record DictionaryRecordDeleted( OffsetDateTime closedAt, OffsetDateTime deletedAt, String deletedBy, - String reason) implements DictionaryRecordEvent {} + String reason) implements DictionaryRecordEvent { + + @com.fasterxml.jackson.annotation.JsonProperty("type") + public String type() { return "RecordDeleted"; } +} diff --git a/ordinis-events-api/src/main/java/cloud/nstart/terravault/ordinis/events/record/DictionaryRecordUpdated.java b/ordinis-events-api/src/main/java/cloud/nstart/terravault/ordinis/events/record/DictionaryRecordUpdated.java index 14769fb..1b05df1 100644 --- a/ordinis-events-api/src/main/java/cloud/nstart/terravault/ordinis/events/record/DictionaryRecordUpdated.java +++ b/ordinis-events-api/src/main/java/cloud/nstart/terravault/ordinis/events/record/DictionaryRecordUpdated.java @@ -23,4 +23,8 @@ public record DictionaryRecordUpdated( OffsetDateTime validFrom, OffsetDateTime validTo, OffsetDateTime updatedAt, - String updatedBy) implements DictionaryRecordEvent {} + String updatedBy) implements DictionaryRecordEvent { + + @com.fasterxml.jackson.annotation.JsonProperty("type") + public String type() { return "RecordUpdated"; } +} diff --git a/ordinis-projection-writer/pom.xml b/ordinis-projection-writer/pom.xml index c0760dd..36d2742 100644 --- a/ordinis-projection-writer/pom.xml +++ b/ordinis-projection-writer/pom.xml @@ -19,6 +19,10 @@ cloud.nstart.terravault.ordinis ordinis-events-api + + cloud.nstart.terravault.ordinis + ordinis-domain + org.springframework.boot @@ -53,5 +57,22 @@ org.springframework.cloud spring-cloud-starter-vault-config + + com.fasterxml.jackson.core + jackson-databind + + + io.micrometer + micrometer-tracing + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + diff --git a/ordinis-projection-writer/src/main/java/cloud/nstart/terravault/ordinis/projection/OrdinisProjectionWriterApplication.java b/ordinis-projection-writer/src/main/java/cloud/nstart/terravault/ordinis/projection/OrdinisProjectionWriterApplication.java new file mode 100644 index 0000000..925bd12 --- /dev/null +++ b/ordinis-projection-writer/src/main/java/cloud/nstart/terravault/ordinis/projection/OrdinisProjectionWriterApplication.java @@ -0,0 +1,16 @@ +package cloud.nstart.terravault.ordinis.projection; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; + +@SpringBootApplication(scanBasePackages = "cloud.nstart.terravault.ordinis") +@EnableJpaRepositories(basePackages = "cloud.nstart.terravault.ordinis") +@EntityScan(basePackages = "cloud.nstart.terravault.ordinis") +public class OrdinisProjectionWriterApplication { + + public static void main(String[] args) { + SpringApplication.run(OrdinisProjectionWriterApplication.class, args); + } +} diff --git a/ordinis-projection-writer/src/main/java/cloud/nstart/terravault/ordinis/projection/ProjectionWriter.java b/ordinis-projection-writer/src/main/java/cloud/nstart/terravault/ordinis/projection/ProjectionWriter.java new file mode 100644 index 0000000..2ee4e39 --- /dev/null +++ b/ordinis-projection-writer/src/main/java/cloud/nstart/terravault/ordinis/projection/ProjectionWriter.java @@ -0,0 +1,122 @@ +package cloud.nstart.terravault.ordinis.projection; + +import cloud.nstart.terravault.ordinis.domain.definition.DictionaryDefinition; +import cloud.nstart.terravault.ordinis.domain.definition.DictionaryDefinitionRepository; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import io.micrometer.core.instrument.Counter; +import io.micrometer.core.instrument.MeterRegistry; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +/** + * Записывает projection в Redis: per-locale сплющенный JSON + raw multi-locale. + * При delete — удаляет все варианты ключей и снимает с индекса. + */ +@Component +public class ProjectionWriter { + + private static final Logger log = LoggerFactory.getLogger(ProjectionWriter.class); + + private final StringRedisTemplate redis; + private final DictionaryDefinitionRepository definitionRepository; + private final ObjectMapper objectMapper; + private final Counter recordsWrittenCounter; + private final Counter recordsDeletedCounter; + + public ProjectionWriter( + StringRedisTemplate redis, + DictionaryDefinitionRepository definitionRepository, + ObjectMapper objectMapper, + MeterRegistry meterRegistry) { + this.redis = redis; + this.definitionRepository = definitionRepository; + this.objectMapper = objectMapper; + this.recordsWrittenCounter = Counter.builder("ordinis_projection_records_written_total") + .description("Записано в Redis projection (per locale)") + .register(meterRegistry); + this.recordsDeletedCounter = Counter.builder("ordinis_projection_records_deleted_total") + .description("Удалено из Redis projection") + .register(meterRegistry); + } + + @Transactional(readOnly = true) + public void upsert(String bundle, String dictionary, String businessKey, JsonNode data) throws Exception { + Optional defOpt = definitionRepository.findByName(dictionary); + if (defOpt.isEmpty()) { + log.warn("Projection upsert: definition '{}' не найдена, skip", dictionary); + return; + } + DictionaryDefinition def = defOpt.get(); + JsonNode schema = def.getSchemaJson(); + String[] locales = def.getSupportedLocales(); + + String rawKey = RedisKeys.recordRaw(bundle, dictionary, businessKey); + redis.opsForValue().set(rawKey, objectMapper.writeValueAsString(data)); + + for (String locale : locales) { + JsonNode flat = flatten(schema, data, locale, def.getDefaultLocale()); + String key = RedisKeys.record(bundle, dictionary, businessKey, locale); + redis.opsForValue().set(key, objectMapper.writeValueAsString(flat)); + recordsWrittenCounter.increment(); + } + + redis.opsForSet().add(RedisKeys.dictionaryIndex(bundle, dictionary), businessKey); + log.debug("Projection upserted: {}:{} ({} locales)", dictionary, businessKey, locales.length); + } + + public void delete(String bundle, String dictionary, String businessKey) { + Set matching = redis.keys(RedisKeys.recordKeyPattern(bundle, dictionary, businessKey)); + if (matching != null && !matching.isEmpty()) { + redis.delete(matching); + recordsDeletedCounter.increment(matching.size()); + } + redis.opsForSet().remove(RedisKeys.dictionaryIndex(bundle, dictionary), businessKey); + log.debug("Projection deleted: {}:{}", dictionary, businessKey); + } + + // --- mirror flattener (упрощённая версия из read-api, чтобы не тянуть зависимость) --- + private JsonNode flatten(JsonNode schema, JsonNode data, String locale, String defaultLocale) { + if (data == null || !data.isObject()) return data; + ObjectNode out = data.deepCopy(); + walk(schema, out, locale, defaultLocale); + return out; + } + + private void walk(JsonNode schema, ObjectNode data, String locale, String defaultLocale) { + if (schema == null || !schema.isObject()) return; + JsonNode properties = schema.get("properties"); + if (properties == null || !properties.isObject()) return; + Iterator> it = properties.fields(); + while (it.hasNext()) { + var e = it.next(); + String name = e.getKey(); + JsonNode fieldSchema = e.getValue(); + JsonNode xLoc = fieldSchema.get("x-localized"); + if (xLoc != null && xLoc.isBoolean() && xLoc.asBoolean()) { + flattenField(data, name, locale, defaultLocale); + } else if (fieldSchema.has("properties") && data.has(name) && data.get(name).isObject()) { + walk(fieldSchema, (ObjectNode) data.get(name), locale, defaultLocale); + } + } + } + + private void flattenField(ObjectNode data, String field, String locale, String defaultLocale) { + JsonNode v = data.get(field); + if (v == null || !v.isObject()) return; + JsonNode picked = v.get(locale); + if (picked == null || picked.isNull()) picked = v.get(defaultLocale); + if (picked == null || picked.isNull()) data.set(field, objectMapper.nullNode()); + else data.set(field, picked); + } +} diff --git a/ordinis-projection-writer/src/main/java/cloud/nstart/terravault/ordinis/projection/RecordEventListener.java b/ordinis-projection-writer/src/main/java/cloud/nstart/terravault/ordinis/projection/RecordEventListener.java new file mode 100644 index 0000000..0dc83c7 --- /dev/null +++ b/ordinis-projection-writer/src/main/java/cloud/nstart/terravault/ordinis/projection/RecordEventListener.java @@ -0,0 +1,89 @@ +package cloud.nstart.terravault.ordinis.projection; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.micrometer.core.instrument.Counter; +import io.micrometer.core.instrument.MeterRegistry; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.kafka.annotation.KafkaListener; +import org.springframework.kafka.support.KafkaHeaders; +import org.springframework.messaging.handler.annotation.Header; +import org.springframework.messaging.handler.annotation.Payload; +import org.springframework.stereotype.Component; + +/** + * Подписан на 3 scope-топика. На RecordCreated/Updated делает upsert + * projection, на Deleted — delete. На Definition events ничего не делает + * (projection writer не материализует definitions). + * + *

Группа consumer'а: {@code ordinis-projection-cuod}. ACL Strimzi даёт + * этому пользователю Read доступ ко всем 3 scope-топикам и Read к группе + * {@code ordinis-projection-*} (см. ordinis-infra/k8s/kafka/topology.yaml). + */ +@Component +public class RecordEventListener { + + private static final Logger log = LoggerFactory.getLogger(RecordEventListener.class); + + private final ProjectionWriter writer; + private final ObjectMapper objectMapper; + private final Counter eventsCounter; + private final Counter errorsCounter; + + public RecordEventListener( + ProjectionWriter writer, ObjectMapper objectMapper, MeterRegistry meterRegistry) { + this.writer = writer; + this.objectMapper = objectMapper; + this.eventsCounter = Counter.builder("ordinis_projection_events_processed_total") + .description("Kafka events обработанные projection writer'ом") + .register(meterRegistry); + this.errorsCounter = Counter.builder("ordinis_projection_events_errors_total") + .description("Ошибки обработки event'a в projection writer'е") + .register(meterRegistry); + } + + @KafkaListener( + topics = { + "ordinis.cuod.events.public", + "ordinis.cuod.events.internal", + "ordinis.cuod.events.restricted" + }, + groupId = "ordinis-projection-cuod", + containerFactory = "kafkaListenerContainerFactory" + ) + public void onMessage( + @Payload String value, + @Header(name = KafkaHeaders.RECEIVED_KEY, required = false) String key) { + try { + JsonNode envelope = objectMapper.readTree(value); + String dictionary = envelope.path("dictionaryName").asText(null); + String bundle = envelope.path("bundle").asText("cuod"); + JsonNode payload = envelope.path("payload"); + String type = payload.path("type").asText(null); + + if (type == null || dictionary == null) { + log.debug("Skip event (no type or dictionary): key={}", key); + return; + } + + String businessKey = payload.path("businessKey").asText(null); + switch (type) { + case "RecordCreated", "RecordUpdated" -> { + if (businessKey == null) return; + JsonNode data = payload.path("data"); + writer.upsert(bundle, dictionary, businessKey, data); + } + case "RecordDeleted" -> { + if (businessKey == null) return; + writer.delete(bundle, dictionary, businessKey); + } + default -> log.debug("Unhandled event type {} for {}", type, dictionary); + } + eventsCounter.increment(); + } catch (Exception e) { + errorsCounter.increment(); + log.error("Failed to process projection event key={}: {}", key, e.getMessage(), e); + } + } +} diff --git a/ordinis-projection-writer/src/main/java/cloud/nstart/terravault/ordinis/projection/RedisKeys.java b/ordinis-projection-writer/src/main/java/cloud/nstart/terravault/ordinis/projection/RedisKeys.java new file mode 100644 index 0000000..2fd7ca6 --- /dev/null +++ b/ordinis-projection-writer/src/main/java/cloud/nstart/terravault/ordinis/projection/RedisKeys.java @@ -0,0 +1,40 @@ +package cloud.nstart.terravault.ordinis.projection; + +/** + * Конвенция ключей в Redis для projection. + * + *

Per-locale flat record:
+ * {@code ord::::}
+ * Value = JSON со сплющенным data на эту локаль (готов к отдаче без processing). + * + *

Multi-locale raw record:
+ * {@code ord::::_raw}
+ * Value = полный multi-locale JSON. Используется когда client запросил {@code Accept-Language: *}. + * + *

Index по dictionary (set):
+ * {@code ord:idx::}
+ * Members = businessKey'и активных записей. Для list endpoint. + * + *

Last seen kafka offset (для idempotency consumer'a):
+ * {@code ord:offset::} + */ +public final class RedisKeys { + + private RedisKeys() {} + + public static String record(String bundle, String dictionary, String businessKey, String locale) { + return "ord:" + bundle + ":" + dictionary + ":" + businessKey + ":" + locale; + } + + public static String recordRaw(String bundle, String dictionary, String businessKey) { + return "ord:" + bundle + ":" + dictionary + ":" + businessKey + ":_raw"; + } + + public static String dictionaryIndex(String bundle, String dictionary) { + return "ord:idx:" + bundle + ":" + dictionary; + } + + public static String recordKeyPattern(String bundle, String dictionary, String businessKey) { + return "ord:" + bundle + ":" + dictionary + ":" + businessKey + ":*"; + } +} diff --git a/ordinis-projection-writer/src/main/resources/application.yml b/ordinis-projection-writer/src/main/resources/application.yml new file mode 100644 index 0000000..d578118 --- /dev/null +++ b/ordinis-projection-writer/src/main/resources/application.yml @@ -0,0 +1,121 @@ +spring: + application: + name: ordinis-projection-writer + profiles: + active: ${SPRING_PROFILES_ACTIVE:dev} + +server: + port: 8082 + shutdown: graceful + +management: + endpoints: + web: + exposure: + include: health,info,prometheus,refresh,metrics + endpoint: + health: + probes: + enabled: true + show-details: when-authorized + group: + liveness: + include: livenessState + readiness: + include: readinessState,redis + prometheus: + metrics: + export: + enabled: true + metrics: + tags: + application: ${spring.application.name} + environment: ${ENVIRONMENT:dev} + +logging: + level: + root: INFO + cloud.nstart.terravault.ordinis: DEBUG + +--- +spring: + config: + activate: + on-profile: dev + cloud: + config: + enabled: false + vault: + enabled: false + autoconfigure: + exclude: + - org.springframework.cloud.vault.config.VaultAutoConfiguration + - org.springframework.cloud.vault.config.VaultObservationAutoConfiguration + - org.springframework.cloud.vault.config.VaultReactiveAutoConfiguration + datasource: + url: jdbc:postgresql://${ORDINIS_PG_HOST:localhost}:${ORDINIS_PG_PORT:5433}/${ORDINIS_PG_DB:ordinis} + username: ${ORDINIS_PG_USER:ordinis_app} + password: ${ORDINIS_PG_PASSWORD:} + driver-class-name: org.postgresql.Driver + hikari: + read-only: true + jpa: + hibernate: + ddl-auto: none + open-in-view: false + data: + redis: + host: ${ORDINIS_REDIS_HOST:localhost} + port: ${ORDINIS_REDIS_PORT:6379} + password: ${ORDINIS_REDIS_PASSWORD:} + timeout: 2s + kafka: + bootstrap-servers: ${ORDINIS_KAFKA_BOOTSTRAP:localhost:32094} + properties: + security.protocol: ${ORDINIS_KAFKA_SECURITY:SASL_PLAINTEXT} + sasl.mechanism: ${ORDINIS_KAFKA_SASL_MECHANISM:SCRAM-SHA-512} + sasl.jaas.config: >- + org.apache.kafka.common.security.scram.ScramLoginModule required + username="${ORDINIS_KAFKA_USER:ordinis-projection-writer}" + password="${ORDINIS_KAFKA_PASSWORD:}"; + consumer: + group-id: ordinis-projection-cuod + auto-offset-reset: earliest + enable-auto-commit: false + key-deserializer: org.apache.kafka.common.serialization.StringDeserializer + value-deserializer: org.apache.kafka.common.serialization.StringDeserializer + listener: + ack-mode: record + +otel: + sdk: + disabled: true + +--- +spring: + config: + activate: + on-profile: k8s + import: + - "configserver:${SPRING_CLOUD_CONFIG_URI:http://config-server.ordinis-dev:8888}" + - "vault://" + cloud: + config: + enabled: true + vault: + enabled: true + uri: ${VAULT_URI:http://vault.config:8200} + authentication: KUBERNETES + kubernetes: + role: ordinis-projection-writer + service-account-token-file: /var/run/secrets/kubernetes.io/serviceaccount/token + kv: + enabled: true + backend: secret + application-name: ordinis/cuod + datasource: + hikari: + read-only: true + jpa: + hibernate: + ddl-auto: none diff --git a/ordinis-projection-writer/src/main/resources/logback-spring.xml b/ordinis-projection-writer/src/main/resources/logback-spring.xml new file mode 100644 index 0000000..8da82bf --- /dev/null +++ b/ordinis-projection-writer/src/main/resources/logback-spring.xml @@ -0,0 +1,35 @@ + + + + + + + + trace_id + span_id + scope + dictionary + {"service":"${appName}","environment":"${environment}"} + + + + + + %d{HH:mm:ss.SSS} %-5level [%thread] %logger{36} - %msg%n + + + + + + + + + + + + + + + + +