package com.zy.acs.common.utils; import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.slf4j.helpers.FormattingTuple; import org.slf4j.helpers.MessageFormatter; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * news stories for vincent * Created by vincent on 2022/12/22 */ @Slf4j public class News { private static final int DEFAULT_CAPACITY = 1024; private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); private static final NewsQueue NEWS_QUEUE = new NewsQueue<>(DEFAULT_CAPACITY); public static void main(String[] args) { News.info("info{}", 1); News.warn("warn{}", 2); News.error("error{}", 3); System.out.println(News.print()); } public static void info(String format, Object... arguments) { log.info(format, arguments); offer(NewsLevel.INFO, format, arguments); } public static void warn(String format, Object... arguments) { log.warn(format, arguments); offer(NewsLevel.WARN, format, arguments); } public static void error(String format, Object... arguments) { log.error(format, arguments); offer(NewsLevel.ERROR, format, arguments); } public static String printStr() { StringBuilder sb = new StringBuilder("["); List domains = NEWS_QUEUE.data(); for (int i = 0; i < domains.size(); i++) { NewsDomain domain = domains.get(i); sb.append("{"); sb.append("\"l\":").append(domain.getLevel().idx).append(","); sb.append("\"v\":\"").append(escapeJson(domain.getContent())).append("\"").append(","); sb.append("\"t\":\"").append(escapeJson(domain.getDate())).append("\""); sb.append("}"); if (i < domains.size() - 1) { sb.append(","); } } sb.append("]"); return sb.toString(); } public static List> print() { List> res = new ArrayList<>(); for (NewsDomain datum : NEWS_QUEUE.data()) { Map map = new HashMap<>(); map.put("l", datum.getLevel().idx); map.put("v", datum.getContent()); map.put("t", datum.getDate()); res.add(map); } return res; } private static boolean offer(NewsLevel level, String msg, Object[] args) { String template = msg == null ? "" : msg; FormattingTuple tuple = MessageFormatter.arrayFormat(template, args); String formatted = tuple.getMessage(); return NEWS_QUEUE.offer(new NewsDomain(level, formatted, LocalDateTime.now().format(DATE_FORMATTER))); } private static String escapeJson(String value) { if (value == null) { return ""; } StringBuilder sb = new StringBuilder(value.length()); for (char c : value.toCharArray()) { switch (c) { case '"': sb.append("\\\""); break; case '\\': sb.append("\\\\"); break; case '\n': sb.append("\\n"); break; case '\r': sb.append("\\r"); break; case '\t': sb.append("\\t"); break; default: if (c < 0x20) { sb.append(String.format("\\u%04x", (int) c)); } else { sb.append(c); } } } return sb.toString(); } private static final class NewsQueue { private final Object[] arr; private final int capacity; private int head; private int size; private NewsQueue(int capacity) { if (capacity <= 0) { throw new IllegalArgumentException("capacity must be > 0"); } this.arr = new Object[capacity]; this.capacity = capacity; this.head = 0; this.size = 0; } public synchronized boolean offer(T t) { int writeIndex = (head + size) % capacity; arr[writeIndex] = t; if (size == capacity) { head = (head + 1) % capacity; } else { size++; } return true; } public synchronized List data() { List copy = new ArrayList<>(size); for (int i = 0; i < size; i++) { int idx = (head + i) % capacity; @SuppressWarnings("unchecked") T element = (T) arr[idx]; copy.add(element); } return copy; } } @Data static class NewsDomain { private final NewsLevel level; private final String content; private final String date; public NewsDomain(NewsLevel level, String content, String date) { this.level = level; this.content = content; this.date = date; } } enum NewsLevel { INFO(1), WARN(2), ERROR(3), ; public final int idx; NewsLevel(int idx) { this.idx = idx; } } }