[컴] gradle java build 에서 git commit hash id 를 추가

스프링 / 스프링부트 / spring /

gradle java build 에서 git commit hash id 를 추가

com.gorylenko.gradle-git-properties gradle plugin 을 이용할 것이다.

build.gralde

아래처럼 build.gradle 을 설정하자. build 를 하면 build/resources/main/git.properties 가 만들어진다.

plugins {
  id "com.gorylenko.gradle-git-properties" version "2.4.1"
}
...
gitProperties {
    keys = ['git.branch','git.commit.id','git.commit.time']
    customProperty 'greeting', 'Hello' // expression
    // Customize file name (could be a file name or a relative file path below gitPropertiesResourceDir dir)
    gitPropertiesName = "my-git-file.properties"
    dotGitDirectory = file("${project.rootDir}/.git") # 정의하지 않으면, 자동으로 .git folder를 찾는다.
}

git.properties

위에서 gitPropertiesName 로 properties file 의 이름을 변경했기에, build/resources/main/my-git-file.properties 가 만들어진다.

my-git-file.properties 의 내용은 다음과 같다.

// build/resources/main/my-git-file.properties
git.branch=mybranch_name
git.commit.id=0c5da9f4f7312bb6c02f21c47ae129b31a006046
git.commit.time=2024-03-10T11\:18\:37+0900
greeting=Hello

jar 에서 위치

그리고 이것은 gradlew build 를 할 때 아래 경로에 들어가게 된다.

  • myjar-0.0.1.jar\BOOT-INF\classes\git.properties

SpringBoot

[컴] amazone 의 Correctto 17

 

amazone 의 Correctto 17

아마존에서 제공하는 OpenJDK 17 로 보면 된다. LTS(Long Term Support) 를 지원한다.

curl -LO https://corretto.aws/downloads/latest/amazon-corretto-17-x64-linux-jdk.tar.gz

아래 링크에 가면, deb, rpm 등도 다운로드 받을 수 있다.

[컴] MySql virtual column

mariadb 마리아db /

MySql virtual column

ALTER TABLE t1 ADD COLUMN c2 INT GENERATED ALWAYS AS (c1 + 1) STORED;
ALTER TABLE t1 ADD COLUMN c2 INT GENERATED ALWAYS AS (c1 + 1) VIRTUAL;

다른 table의 column을 이용하려면, view를 만드는 것도 방법이다.

Reference

  1. Generated (Virtual and Persistent/Stored) Columns - MariaDB Knowledge Base
  2. mysql - Stored/Virtual Generated Column- Pros/Cons/Best Practices? - Stack Overflow

[컴] SpringBoot 3.x 의 logging 설정

logback config / springboot log configuration / log config / spring log / 기간 설정

SpringBoot 3.x 의 logging 설정

spring-boot-starter-web을 사용한다면 logback 이 dependency 에 걸려있어서 자동으로 추가된다.

좀 더 자세한 logback option 의 설명

logback.rollingpolicy.max-history 에 대한 설명

TimeBasedRollingPolicy 를 사용한다면, max-history 는 ’며칠의 log를 보관하는 지를 결정’하는 설정이 된다고 한다.

totalSizeCap 을 설정해놓는 것이 disk usage 를 관리하는데 도움이 될 것이라고 한다.

[[컴] kafka app log 주기적으로 삭제

remove kakfka application log / kafka stream log / 자동 삭제

kafka app log 주기적으로 삭제

kafka log 라고 하면 대체로 data 에서 사용하는 log 로 검색된다. 여기선 kafka app 이 찍는 log 를 이야기 한다. 이것은 log4j 로 되어 있어서 log4j 설정을 잡아주면 된다. MaxFileSize, MaxBackupIndex 를 사용하면 된다.

  • MaxFileSize
  • MaxBackupIndex
log4j.rootLogger=INFO, stdout, kafkaAppender

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=[%d] %p %m (%c)%n

log4j.appender.kafkaAppender=org.apache.log4j.RollingFileAppender
log4j.appender.kafkaAppender.File=${kafka.logs.dir}/server.log
log4j.appender.kafkaAppender.layout=org.apache.log4j.PatternLayout
log4j.appender.kafkaAppender.layout.ConversionPattern=[%d] %p %m (%c)%n
log4j.appender.kafkaAppender.MaxFileSize=500MB
log4j.appender.kafkaAppender.MaxBackupIndex=5
...

DailyRollingFileAppender 에서 사용은 주의하자.

DailyRollingFileAppender 에서 MaxBackupIndex = 3 를 사용한다고 해도, 이것이 3일치 log를 보관하는 것을 이야기하는 것은 아니다. 같은 이름의 log에 대해서 backup 을 3개까지 보관가능하다는 뜻이다.

log.2009-11-07.1, log.2009-11-07.2, log.2009-11-07.3 을 보관한다는 이야기다.

Reference

  1. Kafka logs are not deleted - Ops - Confluent Community
  2. Logging · The Internals of Apache Kafka

[컴] 80열은 지키지 않아도 괜찮다.

80 cols / 80 columns /

80열은 지키지 않아도 괜찮다.

개인적으로도 여러가지 생각이 들었지만, 그래도 80열을 맞추는 것이 가독성을 더 좋게 할 것이라 생각했다. 예를 들면, 신문의 타이포그래피를 봐도 그렇지만, width를 일정수준으로 맞춰놓는다. 이것이 확실히 한눈에 한줄을 보기 쉽게하고, 빠르게 읽을 수 있게 해준다.

이런 장점이 있지만, code 는 그것과는 조금 다르게 보는 것이 맞을 듯 하다. 필자도 때론 한줄에 적는것이 더 좋을 것 같은 코드라인을 본다. 하지만 어떻게 하면 80을 맞출 수 있을까 생각한다.

나만 그러고 있는 듯 하지만, 더이상 80열을 심각하게 고려할 필요는 없을 듯 하다. 유명한 개발자가 한 이야기라 더 설득력이 있게 들린다. (물론 리누스는 리눅스 커널코드에 대한 이야기를 한 것이긴 하다.)

이것때문에 사내 좀 더 젊은 개발자에게 물어본 적이 있는데, 80열을 맞춰야 된다는 생각을 가진 분들은 없더라.^^;;;

간략하게 이야기하자면, 더이상 우리는 작은 터미널화면을 사용하던 시대에 살지 않으며, 더이상 그것을 고려할 이유가 없다고 이야기한다. 80열 터머널을 가진 유저는 wrapping 기능을 이용하면 그만이라고 설명한다.

끊임없이 변한다. 그래서 꾸준히 학습해야 한다. IT세상은 지루할 틈은 없어보인다.

Reference

  1. 80-characters-per-line limits should be terminal, says Linux kernel chief Linus Torvalds • The Register

[컴] spring에서 test container 사용시간 단축하기

 

test container / testcontainer / 스프링에서 / 스프링부트 / springboot / 스프링

spring에서 test container 사용시간 단축하기

test container 를 이용해서 test 를 하면, container 를 load 하는 시간이 오래걸린다. 개인적으로 ssd 가 아닌 hdd 에서 동작하도록 해서 그런지 더 실행시간이 오래걸렸다.

그래서 test 를 자주 하려면, 최대한 test container 의 실행을 줄이는 것이 좋다. 즉, 덜 자주 unload , load 를 하는 것이 낫다.

container 를 1번만 실행

Testcontainer 를 사용하면 test class에 @Testcontainers를 사용하게 된다. 이것은 매 class마다 container 를 실행하게 된다.

대체로 test container 를 이용해서 test 를 하면, container 를 load 하는 시간이 오래걸린다. 개인적으로 ssd 가 아닌 hdd 에서 동작하도록 해서 그런지 더 실행시간이 오래걸렸다.

때문에, test 를 자주 하려면, 최대한 test container 의 실행을 줄이는 것이 좋다. 즉, 덜 자주 unload , load 를 하는 것이 낫다.

그래서 이것을 우리는 이것을 전체 test 에서 1번만 실행하게 할 것이다.

위 글에도 나와 있지만 static variable 에 할당하는 것으로 container 의 생성을 1번으로 만든다.

datasource 의 설정

이것은 ApplicationContextInitializer 를 이용한다.

특정 profile 에서는 현재 띄워져있는 container 를 사용하도록 한다

testcontainer를 부하를 줄이려 1번만 load 한다고 해도, TDD 등을 할때는 속도가 엄청 느리다. 계속 unload/load 가 이뤄지기 때문에 빠른 개발이 어렵다.

그래서 build 시점에는 testcontainer 를 사용해서 test 를 하지만, 개발을 할때는 현재 띄워져 있는 container에 접속해서 test 할 수 있도록 했다.

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ContextConfiguration(initializers = TestcontainersInitializer.class)
class FishApplicationTests {
}
...


public class TestcontainersInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {

    private static final String MIGRATION_LOCATION = "filesystem:db/migration";
    public static final MariaDBContainer<?> mariaDB = new MariaDBContainer<>("mariadb:10.6")
            .withDatabaseName("myapp")
            .withUsername("myapp-user")
            .withUrlParam("useBulkStmts", "false")
            .withPassword("strong-password")
            .withReuse(true);

    public static final LocalStackContainer localStack = new LocalStackContainer(
            DockerImageName.parse("localstack/localstack"))
            .withServices(LocalStackContainer.Service.S3)
            .withReuse(true);

    public static final KafkaContainer kafka = new KafkaContainer(
            DockerImageName.parse("confluentinc/cp-kafka:7.5.0"))
            .withReuse(true);

    /*
     * Static block runs before Spring Boot starts the application context.
     */

    @Override
    public void initialize(@NotNull ConfigurableApplicationContext ctx) {
        // if system.property active profile is test, do not start containers
        final String EXCLUDE_PROFILE = "testnocontainer";
        var activeProfiles = ctx.getEnvironment().getActiveProfiles();
        if (activeProfiles.length <= 0 || !activeProfiles[0].equals(EXCLUDE_PROFILE)) {
            // if active profile is NOT 'testnocontainer'

            // 만약 여러개의 test class 를 실행하면 이부분은 당연히 여러번 실행된다. 하지만 괜찮다.
            // static 이라서 여러번 실행되지 않았다.

            /**
             * This routine runs multiple times, but due to the use of the static variables,
             * it only works once
             */
            /*
             * deepStart() is used to start containers in parallel.
             */
            Startables.deepStart(mariaDB, localStack, kafka).join();
            
            runMigrations(); // run flyway

            // 여기를 실행하지 않는 profile 은 application.xml 의 datasource 쪽을 바라보게 된다.
            // 그러므로 datasource setting 을 해놔야 한다.
            TestPropertyValues.of("spring.datasource.url=" + mariaDB.getJdbcUrl())
                .and("spring.datasource.username=" + mariaDB.getUsername())
                .and("spring.datasource.password=" + mariaDB.getPassword())
                .and("spring.kafka.bootstrap-servers=" + kafka.getBootstrapServers())
                .applyTo(ctx);
        }
    }

    private static void runMigrations() {
        Flyway.configure()
                .dataSource(mariaDB.getJdbcUrl(), mariaDB.getUsername(),
                        mariaDB.getPassword())
                .locations(MIGRATION_LOCATION)
                .schemas(mariaDB.getDatabaseName())
                .baselineOnMigrate(true)
                .load().migrate();
    }
}

See Also

  1. 쿠…sal: [컴] Spring Test 에서 @Sql 을 code 로