Нотатки программістера jconsole

У світі Java існує жодна бібліотека для логування роботи системи. Кожна з них має свої переваги й недоліки, писати про яких можна нескінченно довго. Але найчастіше розробники уникають використання складних систем для ведення логів і користуються перевіреними роками (хоч і не позбавленими недоліків) методами: висновком повідомлень в потоки System.out і System.err.

Нотатки программістера jconsole


У цій статті ми створимо свій компонент для виведення вмісту потоків 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:

Запустивши програму і натиснувши на кнопку, побачимо приблизно таку картину:

Нотатки программістера jconsole


Виглядає все саме так, як нам і хотілося. Але на жаль повідомлення, перевизначених потоків, залишаються тільки в компоненті і не доходять до консолі в IDE. А кольори, якими виводяться повідомлення, наглухо вшиті в код нашого компонента. Давайте виправимо це.

Для цього, по-перше, створимо у нашого потоку поле з посиланням на замінний потік. По-друге напишемо методи для зміни кольору нашого потоку. І по-третє, перед перевизначенням потоків, будемо запам'ятовувати їх і прокидає до них виклики наших реалізацій методів write.

Вихідний код отриманого компонента: