Backend Development 14 min read

BizLog SDK: Usage, Configuration, and Extension Guide for Spring Boot

This article introduces the BizLog SDK for Spring Boot, explaining how to add Maven dependencies, enable logging with @EnableLogRecord, configure log records using @LogRecordAnnotation for success, failure, categories, details, operators, custom parsing functions, and shows how to extend the framework with custom services and implementations.

Architect's Tech Stack
Architect's Tech Stack
Architect's Tech Stack
BizLog SDK: Usage, Configuration, and Extension Guide for Spring Boot

This document provides a comprehensive guide to the BizLog SDK, a logging component designed for Spring Boot applications. It explains the problem the component solves—recording who performed what action on which object at what time—and details its features, usage, and extensibility.

Adding the Maven Dependency

<dependency>
    <groupId>io.github.mouzt</groupId>
    <artifactId>bizlog-sdk</artifactId>
    <version>1.0.1</version>
</dependency>

Enabling the Log Record Switch

@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableTransactionManagement
@EnableLogRecord(tenant = "com.mzt.test")
public class Main {
    public static void main(String[] args) {
        SpringApplication.run(Main.class, args);
    }
}

Basic Log Record Annotation Usage

@LogRecordAnnotation(success = "{{#order.purchaseName}}下了一个订单,购买商品「{{#order.productName}}」,下单结果:{{#_ret}}",
    prefix = LogRecordType.ORDER, bizNo = "{{#order.orderNo}}")
public boolean createOrder(Order order) {
    log.info("【创建订单】orderNo={}", order.getOrderNo());
    // db insert order
    return true;
}

The above example logs a successful order creation. If an exception occurs, a failure log can be recorded:

@LogRecordAnnotation(
    fail = "创建订单失败,失败原因:「{{#_errorMsg}}」",
    success = "{{#order.purchaseName}}下了一个订单,购买商品「{{#order.productName}}」,下单结果:{{#_ret}}",
    prefix = LogRecordType.ORDER, bizNo = "{{#order.orderNo}}")
public boolean createOrder(Order order) {
    log.info("【创建订单】orderNo={}", order.getOrderNo());
    // db insert order
    return true;
}

Additional fields such as category , detail , and operator can be added to the annotation to classify logs, store extra information, or specify the operator:

@LogRecordAnnotation(
    fail = "创建订单失败,失败原因:「{{#_errorMsg}}」",
    category = "MANAGER",
    detail = "{{#order.toString()}}",
    success = "{{#order.purchaseName}}下了一个订单,购买商品「{{#order.productName}}」,下单结果:{{#_ret}}",
    prefix = LogRecordType.ORDER, bizNo = "{{#order.orderNo}}")
public boolean createOrder(Order order) { ... }

To specify the operator manually, include the operator attribute and ensure the method has a corresponding parameter:

@LogRecordAnnotation(
    fail = "创建订单失败,失败原因:「{{#_errorMsg}}」",
    category = "MANAGER_VIEW",
    detail = "{{#order.toString()}}",
    operator = "{{#currentUser}}",
    success = "{{#order.purchaseName}}下了一个订单,购买商品「{{#order.productName}}」,下单结果:{{#_ret}}",
    prefix = LogRecordType.ORDER, bizNo = "{{#order.orderNo}}")
public boolean createOrder(Order order, String currentUser) { ... }

For automatic operator retrieval, implement the IOperatorGetService interface and register it as a Spring bean:

@Configuration
public class LogRecordConfiguration {
    @Bean
    public IOperatorGetService operatorGetService() {
        return () -> Optional.of(OrgUserUtils.getCurrentUser())
            .map(a -> new OperatorDO(a.getMisId()))
            .orElseThrow(() -> new IllegalArgumentException("user is null"));
    }
}

@Service
public class DefaultOperatorGetServiceImpl implements IOperatorGetService {
    @Override
    public OperatorDO getUser() {
        OperatorDO operatorDO = new OperatorDO();
        operatorDO.setOperatorId("SYSTEM");
        return operatorDO;
    }
}

Custom Parsing Functions

To transform log variables, implement IParseFunction . For example, converting an order ID to a readable string:

@Component
public class OrderParseFunction implements IParseFunction {
    @Resource @Lazy private OrderQueryService orderQueryService;
    @Override
    public String functionName() { return "ORDER"; }
    @Override
    public String apply(String value) {
        if (StringUtils.isEmpty(value)) { return value; }
        Order order = orderQueryService.queryOrder(Long.parseLong(value));
        return order.getProductName() + "(" + value + ")";
    }
}

Use the function in the annotation:

@LogRecordAnnotation(success = "更新了订单ORDER{#orderId}},更新内容为...",
    prefix = LogRecordType.ORDER, bizNo = "{{#order.orderNo}}",
    detail = "{{#order.toString()}}")
public boolean update(Long orderId, Order order) { return false; }

SpEL ternary expressions can also be used in log messages:

@LogRecordAnnotation(prefix = LogRecordTypeConstant.CUSTOM_ATTRIBUTE, bizNo = "{{#businessLineId}}",
    success = "{{#disable ? '停用' : '启用'}}了自定义属性{ATTRIBUTE{#attributeId}}")
public CustomAttributeVO disableAttribute(Long businessLineId, Long attributeId, boolean disable) { return xxx; }

Framework Extension Points

Implement custom IOperatorGetService to retrieve the current user from the application context:

@Service
public class DefaultOperatorGetServiceImpl implements IOperatorGetService {
    @Override
    public Operator getUser() {
        return Optional.ofNullable(UserUtils.getUser())
            .map(a -> new Operator(a.getName(), a.getLogin()))
            .orElseThrow(() -> new IllegalArgumentException("user is null"));
    }
}

Implement ILogRecordService to persist logs to a database, Elasticsearch, etc. Only the record method is required for saving:

@Service
public class DbLogRecordServiceImpl implements ILogRecordService {
    @Resource private LogRecordMapper logRecordMapper;
    @Override @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void record(LogRecord logRecord) {
        log.info("【logRecord】log={}", logRecord);
        LogRecordPO logRecordPO = LogRecordPO.toPo(logRecord);
        logRecordMapper.insert(logRecordPO);
    }
    @Override public List
queryLog(String bizKey, Collection
types) { return Lists.newArrayList(); }
    @Override public PageDO
queryLogByBizNo(String bizNo, Collection
types, PageRequestDO pageRequestDO) {
        return logRecordMapper.selectByBizNoAndCategory(bizNo, types, pageRequestDO);
    }
}

Custom parse functions can also be created for user IDs, converting them to readable names:

@Component
public class UserParseFunction implements IParseFunction {
    private final Splitter splitter = Splitter.on(",").trimResults();
    @Resource @Lazy private UserQueryService userQueryService;
    @Override public String functionName() { return "USER"; }
    @Override public String apply(String value) {
        if (StringUtils.isEmpty(value)) { return value; }
        List
userIds = Lists.newArrayList(splitter.split(value));
        List
misDOList = userQueryService.getUserList(userIds);
        Map
userMap = StreamUtil.extractMap(misDOList, User::getId);
        StringBuilder sb = new StringBuilder();
        for (String userId : userIds) {
            sb.append(userId);
            if (userMap.get(userId) != null) {
                sb.append("(").append(userMap.get(userId).getUsername()).append(")");
            }
            sb.append(",");
        }
        return sb.toString().replaceAll(",$", "");
    }
}

Variables available in @LogRecordAnnotation include method parameters, return value ( #_ret ), exception message ( #_errorMsg ), and any SpEL static method calls.

Note that the logging interceptor runs after method execution, so any modifications to method parameters are reflected in the SpEL expressions used for log content.

For the full source code, visit the GitHub repository: https://github.com/mouzt/mzt-biz-log .

JavaaopMavenloggingSpring BootannotationsBizLog
Architect's Tech Stack
Written by

Architect's Tech Stack

Java backend, microservices, distributed systems, containerized programming, and more.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.