У світі Java існує жодна бібліотека для логування роботи системи. Кожна з них має свої переваги й недоліки, писати про яких можна нескінченно довго. Але найчастіше розробники уникають використання складних систем для ведення логів і користуються перевіреними роками (хоч і не позбавленими недоліків) методами: висновком повідомлень в потоки System.out і System.err.
У цій статті ми створимо свій компонент для виведення вмісту потоків System.out і System.err. Я постараюся викладати матеріал якомога детальніше, щоб він був зрозумілий навіть початківцям java програмістам.
Потоки введення-виведення в Java.
Тепер звернемо увагу, що цікавлять нас потоки out і err ми можемо перевизначити за допомогою методів:
Аргументами цих методів є класи java.io.PrintStream, чиї екземпляри з легкістю створюються на основі будь-якої реалізації абстрактного класу OutputStream. Всі спадкоємці OutputStream повинні обов'язково реалізовувати всього один метод:
Таким чином, для вирішення нашої задачі нам необхідно написати клас, що успадковує від OutputStream, реалізувати в ньому метод write і замінити їм стандартні потоки System.out і System.err.
Вибір компонента для виведення повідомлень.
Тепер, коли ми розібралися з тим, як перехоплювати висновок в стандартні потоки, нам необхідно вирішити куди ж його перенаправляти? Насправді відповідь на це питання обмежений лише вашою уявою, але в даній статті для цих цілей ми виберемо візуальний компонент JTextPane з бібліотеки javax.swing.
Компонент JTextPane вкрай функціональний і повне його опис зайняло б занадто багато часу, тому ми опустимо деталі і зосередимося тільки на необхідних нам можливості.
Розробка компонента JConsole.
> І так, давайте визначимося, що конкретно ми хочемо від розроблюваного компоненти:
- При розміщенні компонента в системі, він повинен перевизначати стандартні потоки System.out і System.err виводити їх вміст.
- Всі повідомлення перевизначених потоків не повинні залишатися тільки в компоненті, але і потрапляти в потоки, на які посилалися out і err до перевизначення нашим компонентом (це дозволить як і раніше бачити повідомлення в консолі в IDE).
- Повідомлення різних потоків повинні виводитися різним кольором.
- Кольори для виведення повідомлень повинні бути налаштованим (тобто компонент повинен володіти методами для завдання кольору виведення повідомлень).
Створимо свій компонент, спадщини від JTextPane і назвемо його JConsole:
Напишемо метод для додавання тексту в наш компонент:
Давайте спробуємо розібратися в тому, що відбувається. У першому рядку методу ми створюємо найпростіший набір атрибутів тексту і далі визначаємо в ньому колір, яким буде виводитися текст. У блоці try ми отримуємо індекс останнього символу в поточному тексті нашого документа. А потім додаємо рядок з раніше створеними атрибутами.
Фундамент закладено. Тепер необхідно написати реалізацію OutputStream і підмінити їй стандартні потоки.
При написанні свого потоку виведення ми зобов'язані перевизначити тільки один метод - write (int b). Але виводити рядок по одному символу вкрай не оптимально, тому для оптимізації ми так само перевизначити методи write (byte [] b, int off, int len) і write (byte [] b), а так же додамо властивість, що містить колір, яким будемо виводимо вміст цього потоку.
* Взагалі, для більшої гнучкості, ми могли б в поле зберігати не один тільки колір, але екземпляр AttributeSet, але так як ми вирішили обмежитися в форматуванні тексту тільки кольором, його зберігати і станемо.
Код отриманого класу досить простий, за одним винятком: якщо ви помітили, метод insertText ми викликаємо так, ніби це метод класу ConsoleOutputStream, але насправді це метод JConsole! Такий виклик нам дозволяється робити тільки тому, що ConsoleOutputStream є вкладеним класом для JConsole.
Хоча ще не вся заявлена нами функціональність готова, давайте вже помилуємося на наше творіння! Для цього створимо ще один клас Test, успадковані від JFrame. Додамо на нього наш компонент і кнопку, після натиснення якої ми будемо виводити повідомлення в потік System.out і генерувати помилку, стек викликів перед якою буде друкуватися в System.err:
Запустивши програму і натиснувши на кнопку, побачимо приблизно таку картину:
Виглядає все саме так, як нам і хотілося. Але на жаль повідомлення, перевизначених потоків, залишаються тільки в компоненті і не доходять до консолі в IDE. А кольори, якими виводяться повідомлення, наглухо вшиті в код нашого компонента. Давайте виправимо це.
Для цього, по-перше, створимо у нашого потоку поле з посиланням на замінний потік. По-друге напишемо методи для зміни кольору нашого потоку. І по-третє, перед перевизначенням потоків, будемо запам'ятовувати їх і прокидає до них виклики наших реалізацій методів write.
Вихідний код отриманого компонента: