Что такое ThreadFactory? 🤓
Ответ:
ThreadFactory — это интерфейс для создания новых потоков с возможностью кастомизации.
Единственный метод: Thread newThread(Runnable r). Используется в ExecutorService (например, Executors.newFixedThreadPool(10, myThreadFactory)).
Позволяет задавать: имена потоков (для упрощения отладки), приоритеты, демонический статус, UncaughtExceptionHandler.
Хорошая практика — создавать потоки с понятными именами (например, "db-pool-%d"). Утилита Executors.defaultThreadFactory() возвращает стандартную фабрику.
#собеседование
Ответ:
ThreadFactory — это интерфейс для создания новых потоков с возможностью кастомизации.
Единственный метод: Thread newThread(Runnable r). Используется в ExecutorService (например, Executors.newFixedThreadPool(10, myThreadFactory)).
Позволяет задавать: имена потоков (для упрощения отладки), приоритеты, демонический статус, UncaughtExceptionHandler.
Хорошая практика — создавать потоки с понятными именами (например, "db-pool-%d"). Утилита Executors.defaultThreadFactory() возвращает стандартную фабрику.
#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
История технологии сегодня — 26 мая
ℹ️ Кто родился в этот день
Бори́с Серге́евич Петропа́вловский (14 (26) мая 1898, Курск — 6 ноября 1933, Ленинград) — российский и советский военный инженер-артиллерист, участник Первой мировой и Гражданской войн, один из организаторов разработок ракетной техники в СССР, руководитель Газодинамической лаборатории, главный инженер Ленинградского отделения Реактивного научно-исследовательского института (РНИИ). Именем Б. С. Петропавловского был назван кратер на обратной стороне Луны.
🌐 Знаковые события
1868 — Альфред Нобель подал заявку на американский патент на изобретение динамита.
#Biography #Birth_Date #Events #26мая
Бори́с Серге́евич Петропа́вловский (14 (26) мая 1898, Курск — 6 ноября 1933, Ленинград) — российский и советский военный инженер-артиллерист, участник Первой мировой и Гражданской войн, один из организаторов разработок ракетной техники в СССР, руководитель Газодинамической лаборатории, главный инженер Ленинградского отделения Реактивного научно-исследовательского института (РНИИ). Именем Б. С. Петропавловского был назван кратер на обратной стороне Луны.
1868 — Альфред Нобель подал заявку на американский патент на изобретение динамита.
#Biography #Birth_Date #Events #26мая
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
Раздел 9. Исключения, логирование, отладка
Глава 2. Логирование (Logging)
MDC (Mapped Diagnostic Context) – привязка контекстной информации к логам
В многопоточном серверном приложении один и тот же логгер обрабатывает запросы от множества клиентов параллельно. Стандартный вывод логов представляет собой линейную последовательность сообщений от разных потоков, где запросы перемешаны хаотично. Без дополнительного контекста невозможно отследить, какие сообщения относятся к конкретному пользователю или HTTP-запросу.
Рассмотрим типичный лог без MDC:
По имени потока можно различить запросы, но имена потоков переиспользуются пулом и не несут бизнес-семантики. Для анализа проблемы конкретного пользователя или запроса требуется ручной поиск по временным меткам и косвенным признакам.
Архитектура MDC
MDC — это карта ключ-значение, привязанная к текущему потоку выполнения через
Основные операции:
Контекст MDC автоматически подставляется в лог-сообщения через conversion specifier
Базовое использование
Конфигурация Logback для вывода MDC:
Результат:
Все сообщения, выведенные в рамках одного запроса, содержат идентификаторы пользователя и заказа. Это позволяет фильтровать логи grep-ом или в системах агрегации по конкретным значениям MDC.
#Java #для_новичков #beginner #logging #Log4j2 #Logback #SLF4J
Глава 2. Логирование (Logging)
MDC (Mapped Diagnostic Context) – привязка контекстной информации к логам
В многопоточном серверном приложении один и тот же логгер обрабатывает запросы от множества клиентов параллельно. Стандартный вывод логов представляет собой линейную последовательность сообщений от разных потоков, где запросы перемешаны хаотично. Без дополнительного контекста невозможно отследить, какие сообщения относятся к конкретному пользователю или HTTP-запросу.
Рассмотрим типичный лог без MDC:
2024-01-15 10:23:14 [http-nio-8080-exec-3] INFO OrderService - Processing order
2024-01-15 10:23:14 [http-nio-8080-exec-1] INFO PaymentService - Charging card
2024-01-15 10:23:14 [http-nio-8080-exec-3] WARN InventoryService - Low stock
2024-01-15 10:23:15 [http-nio-8080-exec-2] ERROR OrderService - Payment failed
По имени потока можно различить запросы, но имена потоков переиспользуются пулом и не несут бизнес-семантики. Для анализа проблемы конкретного пользователя или запроса требуется ручной поиск по временным меткам и косвенным признакам.
Архитектура MDC
MDC — это карта ключ-значение, привязанная к текущему потоку выполнения через
ThreadLocal. SLF4J предоставляет статический API класса org.slf4j.MDC, который делегирует реализации (Logback или Log4j2) фактическое хранение контекста. Каждый поток имеет собственную изолированную копию MDC, что гарантирует отсутствие перекрестного загрязнения между параллельными запросами.Основные операции:
MDC.put(String key, String value) — добавляет пару ключ-значение в контекст текущего потокаMDC.get(String key) — извлекает значение по ключуMDC.remove(String key) — удаляет конкретный ключMDC.clear() — очищает весь контекст текущего потокаКонтекст MDC автоматически подставляется в лог-сообщения через conversion specifier
%X{key} в PatternLayout.Базовое использование
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
public class OrderService {
private static final Logger logger = LoggerFactory.getLogger(OrderService.class);
public void processOrder(String orderId, String userId) {
// Установка контекста в начале операции
MDC.put("orderId", orderId);
MDC.put("userId", userId);
MDC.put("operation", "processOrder");
try {
logger.info("Starting order processing");
validateOrder(orderId);
chargePayment(orderId, userId);
updateInventory(orderId);
logger.info("Order processing completed");
} catch (Exception e) {
logger.error("Order processing failed", e);
throw e;
} finally {
// Обязательная очистка контекста
MDC.clear();
}
}
}
Конфигурация Logback для вывода MDC:
<property name="LOG_PATTERN"
value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} [%X{userId}] [%X{orderId}] - %msg%n"/>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${LOG_PATTERN}</pattern>
</encoder>
</appender>
Результат:
2024-01-15 10:23:14.123 [http-nio-8080-exec-3] INFO OrderService [user-42] [order-789] - Starting order processing
2024-01-15 10:23:14.145 [http-nio-8080-exec-3] INFO PaymentService [user-42] [order-789] - Charging payment
2024-01-15 10:23:14.167 [http-nio-8080-exec-3] INFO OrderService [user-42] [order-789] - Order processing completed
Все сообщения, выведенные в рамках одного запроса, содержат идентификаторы пользователя и заказа. Это позволяет фильтровать логи grep-ом или в системах агрегации по конкретным значениям MDC.
#Java #для_новичков #beginner #logging #Log4j2 #Logback #SLF4J
👍1
MDC в веб-приложении: Servlet Filter
В веб-приложении контекст запроса устанавливается в фильтре сервлетов и очищается при завершении обработки. Это гарантирует, что все компоненты, участвующие в обработке запроса — контроллеры, сервисы, репозитории — имеют доступ к единому контексту без явной передачи параметров через сигнатуры методов.
Ключевой момент:
Конфигурация Logback с MDC:
#Java #для_новичков #beginner #logging #Log4j2 #Logback #SLF4J
В веб-приложении контекст запроса устанавливается в фильтре сервлетов и очищается при завершении обработки. Это гарантирует, что все компоненты, участвующие в обработке запроса — контроллеры, сервисы, репозитории — имеют доступ к единому контексту без явной передачи параметров через сигнатуры методов.
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.UUID;
@Component
public class LoggingContextFilter extends OncePerRequestFilter {
private static final String REQUEST_ID_HEADER = "X-Request-Id";
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
// Генерация или извлечение идентификатора запроса
String requestId = request.getHeader(REQUEST_ID_HEADER);
if (requestId == null || requestId.isBlank()) {
requestId = UUID.randomUUID().toString();
}
// Извлечение контекста аутентификации
String userId = extractUserId(request);
String sessionId = request.getSession(false) != null ?
request.getSession().getId() : "anonymous";
// Установка MDC
MDC.put("requestId", requestId);
MDC.put("userId", userId != null ? userId : "anonymous");
MDC.put("sessionId", sessionId);
MDC.put("method", request.getMethod());
MDC.put("uri", request.getRequestURI());
// Проброс requestId в ответ для клиентской трассировки
response.setHeader(REQUEST_ID_HEADER, requestId);
try {
filterChain.doFilter(request, response);
} finally {
// Очистка обязательна, иначе поток из пула вернется с загрязненным контекстом
MDC.clear();
}
}
private String extractUserId(HttpServletRequest request) {
// Извлечение из SecurityContext или токена
return SecurityContextHolder.getContext().getAuthentication() != null ?
SecurityContextHolder.getContext().getAuthentication().getName() : null;
}
}
Ключевой момент:
MDC.clear() находится в блоке finally, что гарантирует очистку даже при исключении. Без этого поток из пула вернется с загрязненным MDC, и следующий запрос, обработанный этим потоком, унаследует чужой контекст.Конфигурация Logback с MDC:
<property name="LOG_PATTERN"
value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} [%X{requestId}] [%X{userId}] [%X{method} %X{uri}] - %msg%n"/>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${LOG_PATTERN}</pattern>
</encoder>
</appender>
#Java #для_новичков #beginner #logging #Log4j2 #Logback #SLF4J
👍3
Распределенная трассировка: propagation MDC
В микросервисной архитектуре запрос проходит через несколько сервисов. Для сквозной трассировки идентификатор запроса должен передаваться между сервисами. Это достигается через HTTP-заголовки или сообщения брокера.
Принимающий сервис извлекает
MDC и асинхронное выполнение
Решение — явное копирование контекста:
В Spring для
MDC.clear() vs MDC.remove()
JSON-формат и MDC
При структурированном логировании в JSON MDC-значения автоматически включаются как отдельные поля:
Результат:
Это позволяет системам агрегации индексировать MDC-поля и выполнять точные фильтры без парсинга строк.
#Java #для_новичков #beginner #logging #Log4j2 #Logback #SLF4J
В микросервисной архитектуре запрос проходит через несколько сервисов. Для сквозной трассировки идентификатор запроса должен передаваться между сервисами. Это достигается через HTTP-заголовки или сообщения брокера.
@Service
public class OrderClient {
private final RestTemplate restTemplate;
public OrderDto getOrder(String orderId) {
String requestId = MDC.get("requestId");
HttpHeaders headers = new HttpHeaders();
headers.set("X-Request-Id", requestId); // Propagation
HttpEntity<Void> entity = new HttpEntity<>(headers);
ResponseEntity<OrderDto> response = restTemplate.exchange(
"http://order-service/orders/" + orderId,
HttpMethod.GET,
entity,
OrderDto.class
);
return response.getBody();
}
}
Принимающий сервис извлекает
X-Request-Id из заголовка и устанавливает в свой MDC через тот же фильтр. Таким образом, все логи по цепочке запросов содержат единый идентификатор, позволяя восстановить полный путь в системах агрегации.MDC и асинхронное выполнение
ThreadLocal привязка MDC создает проблему при асинхронном выполнении: при передаче задачи в другой поток (через CompletableFuture, @Async, пул потоков) контекст MDC не переносится автоматически.// Антипаттерн: MDC теряется при асинхронном выполнении
CompletableFuture.runAsync(() -> {
// MDC пуст — задача выполняется в другом потоке
logger.info("Async task"); // Нет requestId, userId
});
Решение — явное копирование контекста:
Map<String, String> contextMap = MDC.getCopyOfContextMap();
CompletableFuture.runAsync(() -> {
MDC.setContextMap(contextMap); // Восстановление контекста
try {
logger.info("Async task with context");
processAsync();
} finally {
MDC.clear();
}
});
В Spring для
@Async методов можно использовать TaskDecorator:@Configuration
public class AsyncConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setTaskDecorator(new MdcTaskDecorator());
executor.initialize();
return executor;
}
}
public class MdcTaskDecorator implements TaskDecorator {
@Override
public Runnable decorate(Runnable runnable) {
Map<String, String> contextMap = MDC.getCopyOfContextMap();
return () -> {
try {
MDC.setContextMap(contextMap);
runnable.run();
} finally {
MDC.clear();
}
};
}
}
MDC.clear() vs MDC.remove()
MDC.clear() удаляет все ключи из контекста текущего потока. Это предпочтительный метод при завершении обработки запроса, так как гарантирует полную очистку.MDC.remove(String key) удаляет конкретный ключ. Используется, когда часть контекста больше не нужна, но остальная должна сохраняться. Однако в большинстве сценариев проще и безопаснее использовать clear() в блоке finally.JSON-формат и MDC
При структурированном логировании в JSON MDC-значения автоматически включаются как отдельные поля:
<encoder class="net.logstash.logback.encoder.LogstashEncoder"/>
Результат:
{
"@timestamp": "2024-01-15T10:23:14.123+00:00",
"message": "Order processing completed",
"logger_name": "com.example.OrderService",
"level": "INFO",
"thread_name": "http-nio-8080-exec-3",
"requestId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"userId": "user-42",
"sessionId": "session-xyz",
"method": "POST",
"uri": "/api/orders"
}Это позволяет системам агрегации индексировать MDC-поля и выполнять точные фильтры без парсинга строк.
#Java #для_новичков #beginner #logging #Log4j2 #Logback #SLF4J
👍5
Что выведет код?
#Tasks
import com.fasterxml.jackson.databind.ObjectMapper;
public class Task260526 {
public static void main(String[] args) throws Exception {
ObjectMapper mapper = new ObjectMapper();
String json = "{\"name\":\"Buddy\",\"breed\":\"Labrador\"}";
Animal260526 animal = mapper.readValue(json, Animal260526.class);
System.out.println(animal.getClass().getSimpleName());
}
}
interface Animal260526 {
String getName();
}
class Dog260526 implements Animal260526 {
public String name;
public String breed;
public Dog260526() {}
@Override
public String getName() { return name; }
}
#Tasks
👍2
👍2
Что такое short-circuit операторы? 🤓
Ответ:
Логические операторы && (И) и || (ИЛИ) являются короткозамкнутыми (short-circuit).
Это означает, что второй операнд вычисляется только при необходимости.
Для &&: если левый операнд false, всё выражение false, правый не вычисляется.
Для ||: если левый операнд true, всё выражение true, правый не вычисляется.
Это позволяет безопасно писать: if (obj != null && obj.isValid()). В отличие от & и |, которые всегда вычисляют оба операнда.
#собеседование
Ответ:
Логические операторы && (И) и || (ИЛИ) являются короткозамкнутыми (short-circuit).
Это означает, что второй операнд вычисляется только при необходимости.
Для &&: если левый операнд false, всё выражение false, правый не вычисляется.
Для ||: если левый операнд true, всё выражение true, правый не вычисляется.
Это позволяет безопасно писать: if (obj != null && obj.isValid()). В отличие от & и |, которые всегда вычисляют оба операнда.
#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
История технологии сегодня — 27 мая
ℹ️ Кто родился в этот день
Джон Дуглас Кокрофт (англ. John Douglas Cockcroft; 27 мая 1897 — 18 сентября 1967) — британский физик-ядерщик, лауреат Нобелевской премии по физике за 1951 год «за исследовательскую работу по расщеплению атомных ядер с помощью искусственно ускоряемых атомных частиц», совместно с Эрнестом Уолтоном.
🌐 Знаковые события
1930 — Ричард Дрю запатентовал «прозрачную целлофановую липкую ленту» — скотч. Новинку использовали для защиты стекла автомашины при покраске.
1988 — компания Microsoft выпустила Windows 2.1x.
#Biography #Birth_Date #Events #27мая
Джон Дуглас Кокрофт (англ. John Douglas Cockcroft; 27 мая 1897 — 18 сентября 1967) — британский физик-ядерщик, лауреат Нобелевской премии по физике за 1951 год «за исследовательскую работу по расщеплению атомных ядер с помощью искусственно ускоряемых атомных частиц», совместно с Эрнестом Уолтоном.
1930 — Ричард Дрю запатентовал «прозрачную целлофановую липкую ленту» — скотч. Новинку использовали для защиты стекла автомашины при покраске.
1988 — компания Microsoft выпустила Windows 2.1x.
#Biography #Birth_Date #Events #27мая
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
[Совет по Java #042]
Тема:
Проблема: Метод
Если передан ключ, не являющийся
Решение: Для ключей, не реализующих
Объяснение: Сигнатура
При использовании лямбд с несколькими параметрами может потребоваться явное указание типа (как в примере
#Java #советы
Тема:
Comparator.comparing(Function) требует, чтобы ключ сравнения был Comparable.Проблема: Метод
Comparator.comparing(Function<? super T, ? extends U> keyExtractor) предполагает, что тип U (ключ, по которому выполняется сравнение) реализует интерфейс Comparable<U>. Если передан ключ, не являющийся
Comparable (например, пользовательский класс без реализации Comparable, или тип, который его не реализует), код не скомпилируется с ошибкой "inferred type does not conform to equality bounds".Решение: Для ключей, не реализующих
Comparable, используйте перегруженную версию Comparator.comparing(Function keyExtractor, Comparator keyComparator). Она принимает дополнительный компаратор для сравнения ключей. Также можно использовать Comparator.comparingInt, comparingLong, comparingDouble для примитивных типов, которые не требуют Comparable. public class ComparatorComparing {
static class Person {
String name;
int age;
LocalDate birthDate;
Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return name + "(" + age + ")";
}
}
// Класс без Comparable
static class ExternalId {
String prefix;
int number;
ExternalId(String prefix, int number) {
this.prefix = prefix;
this.number = number;
}
@Override
public String toString() {
return prefix + "-" + number;
}
}
public static void main(String[] args) {
List<Person> people = Arrays.asList(
new Person("Alice", 30),
new Person("Bob", 25),
new Person("Charlie", 35)
);
//Работает: String реализует Comparable
people.sort(Comparator.comparing(p -> p.name));
System.out.println("Sorted by name: " + people);
//Работает: int примитив через comparingInt
people.sort(Comparator.comparingInt(p -> p.age));
System.out.println("Sorted by age: " + people);
//Ошибка: ExternalId не реализует Comparable
List<ExternalId> ids = Arrays.asList(
new ExternalId("A", 10),
new ExternalId("B", 5),
new ExternalId("A", 20)
);
// ids.sort(Comparator.comparing(id -> id)); // не скомпилируется!
//Решение 1: явный компаратор для ключа
ids.sort(Comparator.comparing(
(ExternalId id) -> id.prefix,
Comparator.naturalOrder() // для строк
));
System.out.println("Sorted by prefix: " + ids);
//Решение 2: составной компаратор через thenComparing
ids.sort(Comparator
.comparing((ExternalId id) -> id.prefix)
.thenComparingInt(id -> id.number));
System.out.println("Sorted by prefix then number: " + ids);
//Решение 3: собственный компаратор для ключа
Comparator<ExternalId> byNumberDesc = Comparator.comparing(
(ExternalId id) -> id.number,
(n1, n2) -> n2 - n1 // обратный порядок
);
ids.sort(byNumberDesc);
System.out.println("Sorted by number desc: " + ids);
}
}Объяснение: Сигнатура
Comparator.comparing(Function keyExtractor) имеет ограничение <U extends Comparable<? super U>>, которое требует, чтобы тип ключа реализовывал Comparable. Это ограничение гарантирует, что для ключей существует "естественный порядок". Для ключей без естественного порядка (например, пользовательские типы, массивы, или типы с специфической логикой сравнения) необходимо предоставить компаратор явно.При использовании лямбд с несколькими параметрами может потребоваться явное указание типа (как в примере
(ExternalId id) -> id.prefix), чтобы компилятор мог вывести типы.#Java #советы
👍4
Что выведет код?
#Tasks
import java.util.*;
public class Task270526 {
public static void main(String[] args) {
List<Person> people = Arrays.asList(
new Person("Alice", 25),
new Person("Bob", 30),
new Person("Charlie", 20)
);
Comparator<Person> comparator = Comparator.comparing(p -> p.age);
people.sort(comparator);
System.out.println(people);
}
static class Person {
String name;
int age;
Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return name + ":" + age;
}
}
}
#Tasks
👍3
Варианты ответа:
Anonymous Quiz
10%
[Alice:25, Bob:30, Charlie:20]
75%
[Charlie:20, Alice:25, Bob:30]
15%
Ошибка компиляции
0%
Исключение ClassCastException
👍4
Что такое instance initializer (нестатический блок)? Как он работает? 🤓
Ответ:
Нестатический блок инициализации — это { ... } внутри класса, без ключевого слова static.
Он выполняется при создании каждого экземпляра, перед конструктором, но после вызова конструктора суперкласса.
Если есть несколько блоков, они выполняются в порядке объявления сверху вниз. Используется для общих инициализаций, которые нужны для всех конструкторов, или для инициализации анонимных классов.
Например: class A { { System.out.println("init"); } A() {...} }.
#собеседование
Ответ:
Нестатический блок инициализации — это { ... } внутри класса, без ключевого слова static.
Он выполняется при создании каждого экземпляра, перед конструктором, но после вызова конструктора суперкласса.
Если есть несколько блоков, они выполняются в порядке объявления сверху вниз. Используется для общих инициализаций, которые нужны для всех конструкторов, или для инициализации анонимных классов.
Например: class A { { System.out.println("init"); } A() {...} }.
#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
История технологии сегодня — 28 мая
ℹ️ Кто родился в этот день
Милутин Мила́нкович (28 мая 1879, Даль (ныне Хорватия) — 12 декабря 1958, Белград) — австро-венгерский, сербский гражданский инженер, климатолог, геофизик, астрофизик и популяризатор науки.
Известен теорией ледниковых периодов, подразумевающей, что из-за периодических изменений параметров своей орбиты Земля проходит через повторяющиеся ледниковые периоды, в настоящее время известные как циклы Миланковича. Кроме того, дал оценку климатическим условиям на других планетах земной группы. Милутин Миланкович также модифицировал юлианский календарь, разработал новоюлианский календарь.
Мариа́н Смолухо́вский (фон Смолан-Смолуховский, пол. Marian Smoluchowski; 28 мая 1872, Фордер-Брюль, близ Вены — 5 сентября 1917, Краков) — австро-венгерский физик-теоретик польского происхождения.
Уравнения Смолуховского, разработанные им теоретические основы и вычислительные методы стали фундаментом статистической физики и особенно важной сегодня её отрасли, называемой теорией стохастических процессов, развиваемой как физиками, так и математиками. Применения уравнений Смолуховского простираются от физики (как макроскопических, так и субатомных систем) и химии до биологии и технических наук. Многочисленны и практические применения модели Смолуховского, например — индустриальное очищение воды, коагуляция молока, возникновение гелевых барьеров, агрегация гранулоцитов, адгезия лейкоцитов, рост нанотрубок и многое другое.
🌐 Знаковые события
1742 — в Лондоне сооружён первый крытый плавательный бассейн.
#Biography #Birth_Date #Events #28мая
Милутин Мила́нкович (28 мая 1879, Даль (ныне Хорватия) — 12 декабря 1958, Белград) — австро-венгерский, сербский гражданский инженер, климатолог, геофизик, астрофизик и популяризатор науки.
Известен теорией ледниковых периодов, подразумевающей, что из-за периодических изменений параметров своей орбиты Земля проходит через повторяющиеся ледниковые периоды, в настоящее время известные как циклы Миланковича. Кроме того, дал оценку климатическим условиям на других планетах земной группы. Милутин Миланкович также модифицировал юлианский календарь, разработал новоюлианский календарь.
Мариа́н Смолухо́вский (фон Смолан-Смолуховский, пол. Marian Smoluchowski; 28 мая 1872, Фордер-Брюль, близ Вены — 5 сентября 1917, Краков) — австро-венгерский физик-теоретик польского происхождения.
Уравнения Смолуховского, разработанные им теоретические основы и вычислительные методы стали фундаментом статистической физики и особенно важной сегодня её отрасли, называемой теорией стохастических процессов, развиваемой как физиками, так и математиками. Применения уравнений Смолуховского простираются от физики (как макроскопических, так и субатомных систем) и химии до биологии и технических наук. Многочисленны и практические применения модели Смолуховского, например — индустриальное очищение воды, коагуляция молока, возникновение гелевых барьеров, агрегация гранулоцитов, адгезия лейкоцитов, рост нанотрубок и многое другое.
1742 — в Лондоне сооружён первый крытый плавательный бассейн.
#Biography #Birth_Date #Events #28мая
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Раздел 10. Логирование и мониторинг в Java
Глава 2. Логирование (Logging)
Логирование в «Библиотеке»
Часть 1. Подключение и настройка Logback
В
Конфигурация:
Создайте файл
Ключевые моменты:
Условное включение через
#Java #для_новичков #beginner #logging #Log4j2 #Logback #SLF4J #практика
Глава 2. Логирование (Logging)
Логирование в «Библиотеке»
Часть 1. Подключение и настройка Logback
В
pom.xml (если Maven) или убедитесь, что Logback на classpath:<<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.4.14</version>
</dependency>
<<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.9</version>
</dependency>
Конфигурация:
logback.xmlСоздайте файл
src/main/resources/logback.xml:<?xml version="1.0" encoding="UTF-8"?>
<<configuration>
<!-- Переменная для профиля -->
<springProperty scope="context" name="PROFILE" source="spring.profiles.active" defaultValue="default"/>
<!-- Для не-Spring проекта используйте системное свойство -->
<property name="PROFILE" value="${PROFILE:-default}"/>
<!-- Консоль: INFO и выше -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
</appender>
<!-- Файл ошибок: ERROR и выше -->
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/errors.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>logs/errors.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<maxHistory>30</maxHistory>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>10MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n%ex</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>
</appender>
<!-- Файл отладки: DEBUG и выше, только при профиле dev -->
<appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/debug.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>logs/debug.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>7</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} [%X{operation}] - %msg%n</pattern>
</encoder>
</appender>
<!-- Корневой логгер -->
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="ERROR_FILE"/>
</root>
<!-- Логгер приложения -->
<logger name="library" level="INFO">
<!-- DEBUG-файл подключается условно -->
</logger>
<!-- Условное включение DEBUG-файла через профиль -->
<if condition='property("PROFILE").contains("dev")'>
<then>
<root level="DEBUG">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="ERROR_FILE"/>
<appender-ref ref="DEBUG_FILE"/>
</root>
<logger name="library" level="DEBUG"/>
</then>
</if>
</configuration>
Ключевые моменты:
ThresholdFilter отсекает уровни ниже заданногоRollingFileAppender с TimeBasedRollingPolicy — ротация по дате и размеруУсловное включение через
<if> требует janino в зависимостях:<<dependency>
<groupId>org.codehaus.janino</groupId>
<artifactId>janino</artifactId>
<version>3.1.11</version>
</dependency>
#Java #для_новичков #beginner #logging #Log4j2 #Logback #SLF4J #практика
👍4
Часть 2. Логирование операций в Library
Добавление книги: INFO
Удаление книги: WARN
Ошибка парсинга JSON: ERROR
#Java #для_новичков #beginner #logging #Log4j2 #Logback #SLF4J #практика
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.slf4j.Marker;
import org.slf4j.MarkerFactory;
public class Library {
private static final Logger log = LoggerFactory.getLogger(Library.class);
private static final Marker SECURITY = MarkerFactory.getMarker("SECURITY");
private final List<<Book> books = new ArrayList<>();
// ...
}
Добавление книги: INFO
public void addBook(Book book) {
MDC.put("operation", "addBook");
MDC.put("bookId", book.getId());
try {
Objects.requireNonNull(book, "Книга не может быть null");
if (findById(book.getId()).isPresent()) {
log.warn("Попытка добавить дубликат книги: id={}", book.getId());
throw new DuplicateBookException(book.getId());
}
books.add(book);
log.info("Книга добавлена: {} автор={} год={}",
book.getTitle(), book.getAuthor(), book.getYear());
} finally {
MDC.clear();
}
}Удаление книги: WARN
public void removeBook(String bookId) {
MDC.put("operation", "removeBook");
MDC.put("bookId", bookId);
try {
Book removed = findById(bookId)
.orElseThrow(() -> new BookNotFoundException(bookId));
books.remove(removed);
log.warn("Книга удалена: {} (автор={})",
removed.getTitle(), removed.getAuthor());
} finally {
MDC.clear();
}
}Ошибка парсинга JSON: ERROR
private List<<Book> parseBooks(String json) {
try {
return objectMapper.readValue(json, new TypeReference<List<<Book>>() {});
} catch (JsonProcessingException e) {
MDC.put("operation", "parseJson");
log.error("Ошибка парсинга JSON: длина={}, причина={}",
json.length(), e.getOriginalMessage(), e);
throw new LibraryException("Невалидный JSON", e);
} finally {
MDC.clear();
}
}#Java #для_новичков #beginner #logging #Log4j2 #Logback #SLF4J #практика
👍4
Часть 3. MDC для контекста операций
MDC (Mapped Diagnostic Context) — потоколокальное хранилище ключ-значение. Данные доступны в layout через
Обёртка для автоматической очистки
Использование:
Часть 4. Маркер SECURITY для критических операций
Удаление всех книг
Конфигурация обработки маркера
Добавьте в
Часть 5. Защита паролей и чувствительных данных
Проблема
Решение 1: маскирование в toString()
Решение 2: специальный метод для логирования
#Java #для_новичков #beginner #logging #Log4j2 #Logback #SLF4J #практика
MDC (Mapped Diagnostic Context) — потоколокальное хранилище ключ-значение. Данные доступны в layout через
%X{key}.Обёртка для автоматической очистки
public class MdcOperation implements AutoCloseable {
private final String operation;
public MdcOperation(String operation, String userId) {
this.operation = operation;
MDC.put("operation", operation);
MDC.put("userId", userId);
MDC.put("timestamp", Instant.now().toString());
}
@Override
public void close() {
MDC.remove("operation");
MDC.remove("userId");
MDC.remove("timestamp");
}
}Использование:
public void processBatch(List<<Book> newBooks, String userId) {
try (MdcOperation op = new MdcOperation("batchImport", userId)) {
log.info("Начало пакетного импорта: {} книг", newBooks.size());
for (Book book : newBooks) {
try {
addBook(book);
} catch (DuplicateBookException e) {
log.warn("Пропущен дубликат в пакете: {}", book.getId());
}
}
log.info("Завершён пакетный импорт: добавлено {} из {}",
newBooks.size(), newBooks.size());
}
}Часть 4. Маркер SECURITY для критических операций
Удаление всех книг
public void clearAllBooks(String userId, String reason) {
try (MdcOperation op = new MdcOperation("clearAll", userId)) {
int count = books.size();
log.info(SECURITY, "Запрос на удаление всех книг: причина='{}', пользователь={}",
reason, userId);
if (count == 0) {
log.warn("Попытка очистить пустую библиотеку");
return;
}
// Дополнительное подтверждение для критической операции
log.warn(SECURITY, "ВНИМАНИЕ: Удаление {} книг. Требуется подтверждение.", count);
books.clear();
log.error(SECURITY, "Библиотека полностью очищена: {} книг удалено пользователем {}",
count, userId);
}
}Конфигурация обработки маркера
Добавьте в
logback.xml отдельный appender для SECURITY:<<appender name="SECURITY_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/security.log</file>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [SECURITY] %X{userId} %X{operation} - %msg%n</pattern>
</encoder>
<filter class="ch.qos.logback.core.filter.EvaluatorFilter">
<evaluator class="ch.qos.logback.classic.boo.JaninoEventEvaluator">
<expression>return marker != null && marker.contains("SECURITY");</expression>
</evaluator>
<OnMatch>ACCEPT</OnMatch>
<OnMismatch>DENY</OnMismatch>
</filter>
</appender>
<<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="ERROR_FILE"/>
<appender-ref ref="SECURITY_FILE"/>
</root>
Часть 5. Защита паролей и чувствительных данных
Проблема
// Опасно: пароль попадёт в лог
log.info("Авторизация: user={}, password={}", username, password);
Решение 1: маскирование в toString()
public class User {
private String login;
private String passwordHash; // храним хэш, не пароль
private String plainPassword; // только при создании, не логируем
@Override
public String toString() {
return "User[login=" + login + ", password=***]";
}
}Решение 2: специальный метод для логирования
public String toLogString() {
return "User[login=" + login + ", roles=" + roles + "]";
}#Java #для_новичков #beginner #logging #Log4j2 #Logback #SLF4J #практика
👍3
Решение 3: фильтр Logback для маскирования
Применение в
Решение 4: структурный подход — никогда не передавать пароль в лог
Практические задания
Задача 1: настройка окружений
Создайте три конфигурации:
Переключение через системное свойство:
Задача 2: метрики через логи
Добавьте логирование времени выполнения операций:
Соберите статистику по лог-файлу: среднее время, 95-й перцентиль.
Задача 3: аудит изменений
Реализуйте
Все операции
Задача 4: защита от случайного логирования пароля (звёздочка)
Напишите unit-test, который:
- перехватывает лог-вывод через
- проверяет, что при передаче
- падает, если пароль попал в лог чистым текстом
#Java #для_новичков #beginner #logging #Log4j2 #Logback #SLF4J #практика
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.filter.Filter;
import ch.qos.logback.core.spi.FilterReply;
public class PasswordMaskingFilter extends Filter<<ILoggingEvent> {
private static final Pattern PASSWORD_PATTERN =
Pattern.compile("(?i)(password|passwd|pwd)\\s*=\\s*[^\\s,}]+");
@Override
public FilterReply decide(ILoggingEvent event) {
String message = event.getFormattedMessage();
if (PASSWORD_PATTERN.matcher(message).find()) {
// Можно: заменить, отклонить, или передать с предупреждением
return FilterReply.DENY; // Полностью блокируем сообщение с паролем
}
return FilterReply.NEUTRAL;
}
}
Применение в
logback.xml:<<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<filter class="library.logging.PasswordMaskingFilter"/>
<!-- ... -->
</appender>
Решение 4: структурный подход — никогда не передавать пароль в лог
public void authenticate(String login, char[] password) {
// password — массив символов, не строка
log.info("Попытка авторизации: login={}", login);
boolean success = checkPassword(login, password);
if (success) {
log.info("Успешная авторизация: login={}", login);
} else {
log.warn("Неудачная авторизация: login={}", login);
}
// Очистка массива
Arrays.fill(password, '\0');
}Практические задания
Задача 1: настройка окружений
Создайте три конфигурации:
logback.xml — production (INFO в консоль, ERROR в файл, без DEBUG)logback-dev.xml — development (DEBUG в файл, подробный вывод)logback-test.xml — тесты (WARN и выше, минимальный вывод)Переключение через системное свойство:
-Dlogback.configurationFile=logback-dev.xmlЗадача 2: метрики через логи
Добавьте логирование времени выполнения операций:
public void addBookWithMetrics(Book book) {
long start = System.currentTimeMillis();
// ... операция ...
long duration = System.currentTimeMillis() - start;
log.info("Метрика: operation=addBook, durationMs={}", duration);
}Соберите статистику по лог-файлу: среднее время, 95-й перцентиль.
Задача 3: аудит изменений
Реализуйте
AuditLogger — отдельный логгер, записывающий все изменения в неизменяемый лог:<<appender name="AUDIT" class="ch.qos.logback.core.FileAppender">
<file>logs/audit.log</file>
<append>true</append>
<encoder>
<pattern>%d{ISO8601} %X{userId} %X{operation} %msg%n</pattern>
</encoder>
</appender>
Все операции
add, remove, clearAll должны дублироваться в audit-лог.Задача 4: защита от случайного логирования пароля (звёздочка)
Напишите unit-test, который:
- перехватывает лог-вывод через
ListAppender- проверяет, что при передаче
User с паролем в log.info пароль заменён на ***- падает, если пароль попал в лог чистым текстом
#Java #для_новичков #beginner #logging #Log4j2 #Logback #SLF4J #практика
👍4
Что выведет код?
#Tasks
import java.util.Arrays;
import java.util.List;
public class Task280526 {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(-5, -3, -1);
int max = numbers.stream().reduce(0, Integer::max);
System.out.println(max);
}
}
#Tasks
👍2
👍2🆒1
Как объявить константу в Java?🤓
Ответ:
Константа в Java — это переменная, значение которой нельзя изменить.
Объявляется с ключевыми словами static final. Принято имя писать заглавными буквами с подчеркиваниями: public static final double PI = 3.14159;. final означает, что ссылку нельзя переназначить (для примитивов — значение, для объектов — ссылку, но сам объект может быть изменяем).
Для неизменяемых объектов (например, String, LocalDate) final вместе с неизменяемостью даёт полную константу.
#собеседование
Ответ:
Константа в Java
Объявляется с ключевыми словами static final. Принято имя писать заглавными буквами с подчеркиваниями: public static final double PI = 3.14159;. final означает, что ссылку нельзя переназначить (для примитивов — значение, для объектов — ссылку, но сам объект может быть изменяем).
Для неизменяемых объектов (например, String, LocalDate) final вместе с неизменяемостью даёт полную константу.
#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5