Ведение журнала stdin и stdout

Есть ли способ (в идеале простой и элегантный) регистрировать stdin и stdout?

Обратите внимание, что я нет намерен перенаправлять потоки. Я хочу, чтобы стандартные потоки оставались функциональными для связи с другим программным обеспечением, в то время как также записывает все межпроцессное взаимодействие в какой-либо файл.

Должно быть довольно просто это сделать с tee.

Paul R 08.04.2019 14:23

Просто запишите ввод, который вы получаете, в свой файл, а также вывод, который вы записываете? Да, это может быть небольшое дублирование кода, но это облегчит форматирование ведения журнала по вашему желанию или когда-нибудь будет использовать хорошую существующую структуру ведения журнала.

Some programmer dude 08.04.2019 14:24

В противном случае вы можете просмотреть список «Связанных» вопросов, перечисленных в правой колонке здесь.

Some programmer dude 08.04.2019 14:25

«Обратите внимание, что я не собираюсь перенаправлять потоки». Я думаю, это именно то, что вы хотите сделать ... вы хотите, чтобы поток перенаправлялся на ваш регистратор, который затем снова его выдает.

UKMonkey 08.04.2019 14:31

@PaulR На самом деле, я изначально искал решение в C++, но я «тройник» может работать отлично!

Hagadol 08.04.2019 14:33

@UKMonkey Я имел в виду, что записи только что в файл мне недостаточно. Но вы правы, в конце концов я хочу перенаправить его куда-то, где он записан в файл а также output.

Hagadol 08.04.2019 14:36

@Hagadol, вы могли бы создать свой собственный поток, который обертывает cout и файловый поток ... но это будет проще, если вам это сойдет с рук

UKMonkey 08.04.2019 14:38

Можно перенаправить потоки C++ (std::cin, std::cout), манипулируя буферами потоков. Также возможно получение класса из буфера потока и его реализация для записи в два буфера. Насколько я знаю, нет способа переносимого переноса с файловыми потоками C, такими как stdin и stdout (можно перенаправить с помощью freopen(), но нельзя перенаправить на то, что записывает в два выхода). Решения под C не переносимы. В Unix более простым подходом было бы (в зависимости от командной оболочки) использовать командную строку tee input.log | you_program | tee output.log.

Peter 08.04.2019 14:54

@PaulR, использующий «тройник», был самым разумным решением, которое я нашел, поэтому, если вы (или кто-либо другой) готовы ответить на него, я приму его!

Hagadol 08.04.2019 14:56

@Hagadol: рад, что идея работает для вас - я добавил ответ для будущих читателей.

Paul R 08.04.2019 15:01
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
10
621
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Это должно быть довольно просто сделать с tee. Это позволит вам поддерживать любое существующее перенаправление, а также отправлять stdin/stdout в другое место (в файлы в вашем случае). Это также избавляет от необходимости вносить какие-либо изменения в существующий код.

Опция 1:

В качестве @PaulR предлагает вы можете использовать внешний процесс, такой как tee (в Linux/Mac/Unix), или написать свой собственный процесс для чтения из стандартного ввода в цикле и записи в стандартный вывод и в другой файл.

Вариант 2:

Я сделал это много лет назад с std::basic_ios::rdbuf для std::cout. Все, что нужно сделать, это определить класс (см. std::filebuf и std::streambuf):

class tee_buf : public std::filebuf {
   public:
     // Not an owing pointer
     tee_buf(std::streambuf * other_stream) 
        : m_other_stream(other_stream) {}
     void swap( tee_buf& rhs );
     // No need to override open/close since we need to manage only the file.
     // The open/close calls don't touch the other_stream.
   protected:
     int_type overflow(int_type c = traits_type::eof()) override;

     // The parent calls this->overflow(), but then still need to call
     // m_other_stream->sync(). This is problematic since m_other_stream
     // gets flushed twice.
     int sync() override;

     pos_type seekoff( off_type off,
                      std::ios_base::seekdir dir,
                      std::ios_base::openmode which) override {
        return pos_type(off_type(-1)); // ???
     }
     pos_type seekpos( pos_type sp,
                      std::ios_base::openmode which) override {
        return pos_type(off_type(-1)); // ???
     }
     ....

Это более эффективно для интенсивного ввода-вывода, поскольку позволяет избежать посредников. Но в большинстве случаев тройниковое решение проще и предпочтительнее. Если производительность является проблемой (а в большинстве случаев это не так), то можно сделать так, чтобы оба буфера потока совместно использовали один буфер памяти. Также можно использовать асинхронный ввод-вывод для параллельной записи в оба потока.

Использование с утечкой памяти:

std::cout.rdbuf(new tee_buf(std::cout.rdbuf());

Использование без утечки памяти:

Напишите класс RAII, содержащий tee_buf, чтобы сохранить оригинал и установить новый std::cout.rdbuf(). При разрушении восстановить состояние std::cout.rdbuf(). Создайте единственный экземпляр этого класса, который будет выполнять грязную работу по его созданию и уничтожению.

Что касается стиля C stdout: я не верю, что есть способ переопределить его поведение. Максимум можно поиграться с буферной памятью, но этого недостаточно для получения желаемого функционала. Единственное, что можно сделать с stdout, — это использовать tee-подобное решение.

Спасибо за ваш ответ! Вариант 2 больше похож на то, что я изначально намеревался сделать; но теперь я верю, что первый вариант более элегантен в контексте моего приложения. Это, однако, не умаляет полезности вашего ответа для других читателей!

Hagadol 08.04.2019 18:54

Другие вопросы по теме