├── .editorconfig ├── .github ├── CODEOWNERS ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md └── workflows │ ├── android.yml │ └── publish-release.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── RELEASING.md ├── assets ├── logo.png └── logo_512.png ├── build.gradle.kts ├── buildSrc ├── build.gradle.kts └── src │ └── main │ └── java │ └── Dependencies.kt ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── logcat ├── api │ └── logcat.api ├── build.gradle.kts ├── gradle.properties └── src │ ├── main │ ├── AndroidManifest.xml │ └── java │ │ └── logcat │ │ ├── AndroidLogcatLogger.kt │ │ ├── LogPriority.kt │ │ ├── Logcat.kt │ │ ├── LogcatLogger.kt │ │ ├── PrintLogger.kt │ │ └── Throwables.kt │ └── test │ └── java │ └── logcat │ ├── LogcatTest.kt │ └── TestLogcatLogger.kt ├── sample ├── build.gradle.kts └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── squareup │ │ └── logcat │ │ └── sample │ │ ├── ExampleApplication.kt │ │ └── MainActivity.kt │ └── res │ ├── layout │ └── main.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ └── mipmap-xxxhdpi │ └── ic_launcher.png └── settings.gradle.kts /.editorconfig: -------------------------------------------------------------------------------- 1 | # IntelliJ allows to export the code style in the .editorconfig format. These are the settings 2 | # from the most recent settings for SquareAndroid from here: 3 | # 4 | # https://github.com/square/java-code-styles 5 | # 6 | # Every time someone changes Square's code style we should update the config here, too. These 7 | # settings override the selected code style. This means nobody needs to import the code style 8 | # anymore and we can change settings for all developers here at once. 9 | # 10 | # Some keys were manually changed: 11 | # * insert_final_newline (is false, changed to true, expected by KtLint) 12 | # * ktlint_ignore_back_ticked_identifier (enabled, see comment below) 13 | # * root (see comment below) 14 | 15 | # Tells tools to stop searching upwards. 16 | root = true 17 | 18 | [*] 19 | charset = utf-8 20 | end_of_line = lf 21 | indent_size = 2 22 | indent_style = space 23 | insert_final_newline = true 24 | max_line_length = 100 25 | tab_width = 2 26 | ij_continuation_indent_size = 4 27 | ij_formatter_off_tag = @formatter:off 28 | ij_formatter_on_tag = @formatter:on 29 | ij_formatter_tags_enabled = false 30 | ij_smart_tabs = false 31 | ij_wrap_on_typing = false 32 | 33 | # Back-ticked method names are allowed to exceed the line length. That's especially helpful for 34 | # test methods with descriptive names. 35 | ktlint_ignore_back_ticked_identifier=true 36 | 37 | [*.java] 38 | ij_java_align_consecutive_assignments = false 39 | ij_java_align_consecutive_variable_declarations = false 40 | ij_java_align_group_field_declarations = false 41 | ij_java_align_multiline_annotation_parameters = false 42 | ij_java_align_multiline_array_initializer_expression = false 43 | ij_java_align_multiline_assignment = false 44 | ij_java_align_multiline_binary_operation = false 45 | ij_java_align_multiline_chained_methods = false 46 | ij_java_align_multiline_extends_list = false 47 | ij_java_align_multiline_for = false 48 | ij_java_align_multiline_method_parentheses = false 49 | ij_java_align_multiline_parameters = false 50 | ij_java_align_multiline_parameters_in_calls = false 51 | ij_java_align_multiline_parenthesized_expression = false 52 | ij_java_align_multiline_records = true 53 | ij_java_align_multiline_resources = true 54 | ij_java_align_multiline_ternary_operation = false 55 | ij_java_align_multiline_text_blocks = false 56 | ij_java_align_multiline_throws_list = false 57 | ij_java_align_subsequent_simple_methods = false 58 | ij_java_align_throws_keyword = false 59 | ij_java_annotation_parameter_wrap = off 60 | ij_java_array_initializer_new_line_after_left_brace = true 61 | ij_java_array_initializer_right_brace_on_new_line = true 62 | ij_java_array_initializer_wrap = normal 63 | ij_java_assert_statement_colon_on_next_line = false 64 | ij_java_assert_statement_wrap = normal 65 | ij_java_assignment_wrap = normal 66 | ij_java_binary_operation_sign_on_next_line = true 67 | ij_java_binary_operation_wrap = on_every_item 68 | ij_java_blank_lines_after_anonymous_class_header = 0 69 | ij_java_blank_lines_after_class_header = 0 70 | ij_java_blank_lines_after_imports = 1 71 | ij_java_blank_lines_after_package = 1 72 | ij_java_blank_lines_around_class = 1 73 | ij_java_blank_lines_around_field = 0 74 | ij_java_blank_lines_around_field_in_interface = 0 75 | ij_java_blank_lines_around_initializer = 1 76 | ij_java_blank_lines_around_method = 1 77 | ij_java_blank_lines_around_method_in_interface = 1 78 | ij_java_blank_lines_before_class_end = 0 79 | ij_java_blank_lines_before_imports = 1 80 | ij_java_blank_lines_before_method_body = 0 81 | ij_java_blank_lines_before_package = 0 82 | ij_java_block_brace_style = end_of_line 83 | ij_java_block_comment_at_first_column = false 84 | ij_java_call_parameters_new_line_after_left_paren = false 85 | ij_java_call_parameters_right_paren_on_new_line = false 86 | ij_java_call_parameters_wrap = normal 87 | ij_java_case_statement_on_separate_line = true 88 | ij_java_catch_on_new_line = false 89 | ij_java_class_annotation_wrap = normal 90 | ij_java_class_brace_style = end_of_line 91 | ij_java_class_count_to_use_import_on_demand = 999 92 | ij_java_class_names_in_javadoc = 3 93 | ij_java_do_not_indent_top_level_class_members = false 94 | ij_java_do_not_wrap_after_single_annotation = false 95 | ij_java_do_while_brace_force = if_multiline 96 | ij_java_doc_add_blank_line_after_description = true 97 | ij_java_doc_add_blank_line_after_param_comments = false 98 | ij_java_doc_add_blank_line_after_return = false 99 | ij_java_doc_add_p_tag_on_empty_lines = false 100 | ij_java_doc_align_exception_comments = false 101 | ij_java_doc_align_param_comments = false 102 | ij_java_doc_do_not_wrap_if_one_line = true 103 | ij_java_doc_enable_formatting = true 104 | ij_java_doc_enable_leading_asterisks = true 105 | ij_java_doc_indent_on_continuation = false 106 | ij_java_doc_keep_empty_lines = true 107 | ij_java_doc_keep_empty_parameter_tag = false 108 | ij_java_doc_keep_empty_return_tag = false 109 | ij_java_doc_keep_empty_throws_tag = true 110 | ij_java_doc_keep_invalid_tags = true 111 | ij_java_doc_param_description_on_new_line = false 112 | ij_java_doc_preserve_line_breaks = true 113 | ij_java_doc_use_throws_not_exception_tag = true 114 | ij_java_else_on_new_line = false 115 | ij_java_enum_constants_wrap = off 116 | ij_java_extends_keyword_wrap = normal 117 | ij_java_extends_list_wrap = normal 118 | ij_java_field_annotation_wrap = normal 119 | ij_java_finally_on_new_line = false 120 | ij_java_for_brace_force = never 121 | ij_java_for_statement_new_line_after_left_paren = false 122 | ij_java_for_statement_right_paren_on_new_line = false 123 | ij_java_for_statement_wrap = normal 124 | ij_java_generate_final_locals = false 125 | ij_java_generate_final_parameters = false 126 | ij_java_if_brace_force = if_multiline 127 | ij_java_imports_layout = *,|,$* 128 | ij_java_indent_case_from_switch = true 129 | ij_java_insert_inner_class_imports = false 130 | ij_java_insert_override_annotation = true 131 | ij_java_keep_blank_lines_before_right_brace = 0 132 | ij_java_keep_blank_lines_between_package_declaration_and_header = 2 133 | ij_java_keep_blank_lines_in_code = 1 134 | ij_java_keep_blank_lines_in_declarations = 1 135 | ij_java_keep_control_statement_in_one_line = true 136 | ij_java_keep_first_column_comment = false 137 | ij_java_keep_indents_on_empty_lines = false 138 | ij_java_keep_line_breaks = true 139 | ij_java_keep_multiple_expressions_in_one_line = false 140 | ij_java_keep_simple_blocks_in_one_line = false 141 | ij_java_keep_simple_classes_in_one_line = false 142 | ij_java_keep_simple_lambdas_in_one_line = false 143 | ij_java_keep_simple_methods_in_one_line = false 144 | ij_java_label_indent_absolute = false 145 | ij_java_label_indent_size = 0 146 | ij_java_lambda_brace_style = end_of_line 147 | ij_java_layout_static_imports_separately = true 148 | ij_java_line_comment_add_space = false 149 | ij_java_line_comment_at_first_column = false 150 | ij_java_method_annotation_wrap = normal 151 | ij_java_method_brace_style = end_of_line 152 | ij_java_method_call_chain_wrap = on_every_item 153 | ij_java_method_parameters_new_line_after_left_paren = false 154 | ij_java_method_parameters_right_paren_on_new_line = false 155 | ij_java_method_parameters_wrap = normal 156 | ij_java_modifier_list_wrap = false 157 | ij_java_names_count_to_use_import_on_demand = 999 158 | ij_java_new_line_after_lparen_in_record_header = false 159 | ij_java_parameter_annotation_wrap = normal 160 | ij_java_parentheses_expression_new_line_after_left_paren = false 161 | ij_java_parentheses_expression_right_paren_on_new_line = false 162 | ij_java_place_assignment_sign_on_next_line = false 163 | ij_java_prefer_longer_names = true 164 | ij_java_prefer_parameters_wrap = false 165 | ij_java_record_components_wrap = normal 166 | ij_java_repeat_synchronized = true 167 | ij_java_replace_instanceof_and_cast = false 168 | ij_java_replace_null_check = true 169 | ij_java_replace_sum_lambda_with_method_ref = true 170 | ij_java_resource_list_new_line_after_left_paren = false 171 | ij_java_resource_list_right_paren_on_new_line = false 172 | ij_java_resource_list_wrap = normal 173 | ij_java_rparen_on_new_line_in_record_header = false 174 | ij_java_space_after_closing_angle_bracket_in_type_argument = false 175 | ij_java_space_after_colon = true 176 | ij_java_space_after_comma = true 177 | ij_java_space_after_comma_in_type_arguments = true 178 | ij_java_space_after_for_semicolon = true 179 | ij_java_space_after_quest = true 180 | ij_java_space_after_type_cast = true 181 | ij_java_space_before_annotation_array_initializer_left_brace = false 182 | ij_java_space_before_annotation_parameter_list = false 183 | ij_java_space_before_array_initializer_left_brace = true 184 | ij_java_space_before_catch_keyword = true 185 | ij_java_space_before_catch_left_brace = true 186 | ij_java_space_before_catch_parentheses = true 187 | ij_java_space_before_class_left_brace = true 188 | ij_java_space_before_colon = true 189 | ij_java_space_before_colon_in_foreach = true 190 | ij_java_space_before_comma = false 191 | ij_java_space_before_do_left_brace = true 192 | ij_java_space_before_else_keyword = true 193 | ij_java_space_before_else_left_brace = true 194 | ij_java_space_before_finally_keyword = true 195 | ij_java_space_before_finally_left_brace = true 196 | ij_java_space_before_for_left_brace = true 197 | ij_java_space_before_for_parentheses = true 198 | ij_java_space_before_for_semicolon = false 199 | ij_java_space_before_if_left_brace = true 200 | ij_java_space_before_if_parentheses = true 201 | ij_java_space_before_method_call_parentheses = false 202 | ij_java_space_before_method_left_brace = true 203 | ij_java_space_before_method_parentheses = false 204 | ij_java_space_before_opening_angle_bracket_in_type_parameter = false 205 | ij_java_space_before_quest = true 206 | ij_java_space_before_switch_left_brace = true 207 | ij_java_space_before_switch_parentheses = true 208 | ij_java_space_before_synchronized_left_brace = true 209 | ij_java_space_before_synchronized_parentheses = true 210 | ij_java_space_before_try_left_brace = true 211 | ij_java_space_before_try_parentheses = true 212 | ij_java_space_before_type_parameter_list = false 213 | ij_java_space_before_while_keyword = true 214 | ij_java_space_before_while_left_brace = true 215 | ij_java_space_before_while_parentheses = true 216 | ij_java_space_inside_one_line_enum_braces = false 217 | ij_java_space_within_empty_array_initializer_braces = false 218 | ij_java_space_within_empty_method_call_parentheses = false 219 | ij_java_space_within_empty_method_parentheses = false 220 | ij_java_spaces_around_additive_operators = true 221 | ij_java_spaces_around_assignment_operators = true 222 | ij_java_spaces_around_bitwise_operators = true 223 | ij_java_spaces_around_equality_operators = true 224 | ij_java_spaces_around_lambda_arrow = true 225 | ij_java_spaces_around_logical_operators = true 226 | ij_java_spaces_around_method_ref_dbl_colon = false 227 | ij_java_spaces_around_multiplicative_operators = true 228 | ij_java_spaces_around_relational_operators = true 229 | ij_java_spaces_around_shift_operators = true 230 | ij_java_spaces_around_type_bounds_in_type_parameters = true 231 | ij_java_spaces_around_unary_operator = false 232 | ij_java_spaces_within_angle_brackets = false 233 | ij_java_spaces_within_annotation_parentheses = false 234 | ij_java_spaces_within_array_initializer_braces = true 235 | ij_java_spaces_within_braces = false 236 | ij_java_spaces_within_brackets = false 237 | ij_java_spaces_within_cast_parentheses = false 238 | ij_java_spaces_within_catch_parentheses = false 239 | ij_java_spaces_within_for_parentheses = false 240 | ij_java_spaces_within_if_parentheses = false 241 | ij_java_spaces_within_method_call_parentheses = false 242 | ij_java_spaces_within_method_parentheses = false 243 | ij_java_spaces_within_parentheses = false 244 | ij_java_spaces_within_switch_parentheses = false 245 | ij_java_spaces_within_synchronized_parentheses = false 246 | ij_java_spaces_within_try_parentheses = false 247 | ij_java_spaces_within_while_parentheses = false 248 | ij_java_special_else_if_treatment = true 249 | ij_java_subclass_name_suffix = Impl 250 | ij_java_ternary_operation_signs_on_next_line = true 251 | ij_java_ternary_operation_wrap = normal 252 | ij_java_test_name_suffix = Test 253 | ij_java_throws_keyword_wrap = normal 254 | ij_java_throws_list_wrap = normal 255 | ij_java_use_external_annotations = false 256 | ij_java_use_fq_class_names = false 257 | ij_java_use_relative_indents = false 258 | ij_java_use_single_class_imports = true 259 | ij_java_variable_annotation_wrap = normal 260 | ij_java_visibility = public 261 | ij_java_while_brace_force = if_multiline 262 | ij_java_while_on_new_line = false 263 | ij_java_wrap_comments = true 264 | ij_java_wrap_first_method_in_call_chain = false 265 | ij_java_wrap_long_lines = false 266 | 267 | [*.properties] 268 | ij_properties_align_group_field_declarations = false 269 | ij_properties_keep_blank_lines = false 270 | ij_properties_key_value_delimiter = equals 271 | ij_properties_spaces_around_key_value_delimiter = false 272 | 273 | [.editorconfig] 274 | ij_editorconfig_align_group_field_declarations = false 275 | ij_editorconfig_space_after_colon = false 276 | ij_editorconfig_space_after_comma = true 277 | ij_editorconfig_space_before_colon = false 278 | ij_editorconfig_space_before_comma = false 279 | ij_editorconfig_spaces_around_assignment_operators = true 280 | 281 | [{*.ant,*.fxml,*.jhm,*.jnlp,*.jrxml,*.jspx,*.pom,*.rng,*.tagx,*.tld,*.wsdl,*.xml,*.xsd,*.xsl,*.xslt,*.xul}] 282 | ij_xml_align_attributes = false 283 | ij_xml_align_text = false 284 | ij_xml_attribute_wrap = normal 285 | ij_xml_block_comment_at_first_column = true 286 | ij_xml_keep_blank_lines = 2 287 | ij_xml_keep_indents_on_empty_lines = false 288 | ij_xml_keep_line_breaks = true 289 | ij_xml_keep_line_breaks_in_text = true 290 | ij_xml_keep_whitespaces = false 291 | ij_xml_keep_whitespaces_around_cdata = preserve 292 | ij_xml_keep_whitespaces_inside_cdata = false 293 | ij_xml_line_comment_at_first_column = true 294 | ij_xml_space_after_tag_name = false 295 | ij_xml_space_around_equals_in_attribute = false 296 | ij_xml_space_inside_empty_tag = false 297 | ij_xml_text_wrap = normal 298 | ij_xml_use_custom_settings = true 299 | 300 | [{*.bash,*.sh,*.zsh}] 301 | ij_shell_binary_ops_start_line = false 302 | ij_shell_keep_column_alignment_padding = false 303 | ij_shell_minify_program = false 304 | ij_shell_redirect_followed_by_space = false 305 | ij_shell_switch_cases_indented = false 306 | 307 | [{*.gant,*.gradle,*.groovy,*.gy}] 308 | ij_groovy_align_group_field_declarations = false 309 | ij_groovy_align_multiline_array_initializer_expression = false 310 | ij_groovy_align_multiline_assignment = false 311 | ij_groovy_align_multiline_binary_operation = false 312 | ij_groovy_align_multiline_chained_methods = false 313 | ij_groovy_align_multiline_extends_list = false 314 | ij_groovy_align_multiline_for = false 315 | ij_groovy_align_multiline_list_or_map = false 316 | ij_groovy_align_multiline_method_parentheses = false 317 | ij_groovy_align_multiline_parameters = false 318 | ij_groovy_align_multiline_parameters_in_calls = false 319 | ij_groovy_align_multiline_resources = true 320 | ij_groovy_align_multiline_ternary_operation = false 321 | ij_groovy_align_multiline_throws_list = false 322 | ij_groovy_align_named_args_in_map = false 323 | ij_groovy_align_throws_keyword = false 324 | ij_groovy_array_initializer_new_line_after_left_brace = false 325 | ij_groovy_array_initializer_right_brace_on_new_line = false 326 | ij_groovy_array_initializer_wrap = off 327 | ij_groovy_assert_statement_wrap = normal 328 | ij_groovy_assignment_wrap = normal 329 | ij_groovy_binary_operation_wrap = on_every_item 330 | ij_groovy_blank_lines_after_class_header = 0 331 | ij_groovy_blank_lines_after_imports = 1 332 | ij_groovy_blank_lines_after_package = 1 333 | ij_groovy_blank_lines_around_class = 1 334 | ij_groovy_blank_lines_around_field = 0 335 | ij_groovy_blank_lines_around_field_in_interface = 0 336 | ij_groovy_blank_lines_around_method = 1 337 | ij_groovy_blank_lines_around_method_in_interface = 1 338 | ij_groovy_blank_lines_before_imports = 1 339 | ij_groovy_blank_lines_before_method_body = 0 340 | ij_groovy_blank_lines_before_package = 0 341 | ij_groovy_block_brace_style = end_of_line 342 | ij_groovy_block_comment_at_first_column = true 343 | ij_groovy_call_parameters_new_line_after_left_paren = false 344 | ij_groovy_call_parameters_right_paren_on_new_line = false 345 | ij_groovy_call_parameters_wrap = normal 346 | ij_groovy_catch_on_new_line = false 347 | ij_groovy_class_annotation_wrap = normal 348 | ij_groovy_class_brace_style = end_of_line 349 | ij_groovy_class_count_to_use_import_on_demand = 5 350 | ij_groovy_do_while_brace_force = never 351 | ij_groovy_else_on_new_line = false 352 | ij_groovy_enum_constants_wrap = off 353 | ij_groovy_extends_keyword_wrap = normal 354 | ij_groovy_extends_list_wrap = normal 355 | ij_groovy_field_annotation_wrap = normal 356 | ij_groovy_finally_on_new_line = false 357 | ij_groovy_for_brace_force = never 358 | ij_groovy_for_statement_new_line_after_left_paren = false 359 | ij_groovy_for_statement_right_paren_on_new_line = false 360 | ij_groovy_for_statement_wrap = normal 361 | ij_groovy_if_brace_force = if_multiline 362 | ij_groovy_import_annotation_wrap = 2 363 | ij_groovy_imports_layout = *,|,javax.**,java.**,|,$* 364 | ij_groovy_indent_case_from_switch = true 365 | ij_groovy_indent_label_blocks = true 366 | ij_groovy_insert_inner_class_imports = false 367 | ij_groovy_keep_blank_lines_before_right_brace = 0 368 | ij_groovy_keep_blank_lines_in_code = 1 369 | ij_groovy_keep_blank_lines_in_declarations = 1 370 | ij_groovy_keep_control_statement_in_one_line = true 371 | ij_groovy_keep_first_column_comment = false 372 | ij_groovy_keep_indents_on_empty_lines = false 373 | ij_groovy_keep_line_breaks = true 374 | ij_groovy_keep_multiple_expressions_in_one_line = false 375 | ij_groovy_keep_simple_blocks_in_one_line = false 376 | ij_groovy_keep_simple_classes_in_one_line = true 377 | ij_groovy_keep_simple_lambdas_in_one_line = true 378 | ij_groovy_keep_simple_methods_in_one_line = true 379 | ij_groovy_label_indent_absolute = false 380 | ij_groovy_label_indent_size = 0 381 | ij_groovy_lambda_brace_style = end_of_line 382 | ij_groovy_layout_static_imports_separately = true 383 | ij_groovy_line_comment_add_space = false 384 | ij_groovy_line_comment_at_first_column = true 385 | ij_groovy_method_annotation_wrap = normal 386 | ij_groovy_method_brace_style = end_of_line 387 | ij_groovy_method_call_chain_wrap = on_every_item 388 | ij_groovy_method_parameters_new_line_after_left_paren = false 389 | ij_groovy_method_parameters_right_paren_on_new_line = false 390 | ij_groovy_method_parameters_wrap = normal 391 | ij_groovy_modifier_list_wrap = false 392 | ij_groovy_names_count_to_use_import_on_demand = 3 393 | ij_groovy_parameter_annotation_wrap = normal 394 | ij_groovy_parentheses_expression_new_line_after_left_paren = false 395 | ij_groovy_parentheses_expression_right_paren_on_new_line = false 396 | ij_groovy_prefer_parameters_wrap = false 397 | ij_groovy_resource_list_new_line_after_left_paren = false 398 | ij_groovy_resource_list_right_paren_on_new_line = false 399 | ij_groovy_resource_list_wrap = off 400 | ij_groovy_space_after_assert_separator = true 401 | ij_groovy_space_after_colon = true 402 | ij_groovy_space_after_comma = true 403 | ij_groovy_space_after_comma_in_type_arguments = true 404 | ij_groovy_space_after_for_semicolon = true 405 | ij_groovy_space_after_quest = true 406 | ij_groovy_space_after_type_cast = true 407 | ij_groovy_space_before_annotation_parameter_list = false 408 | ij_groovy_space_before_array_initializer_left_brace = false 409 | ij_groovy_space_before_assert_separator = false 410 | ij_groovy_space_before_catch_keyword = true 411 | ij_groovy_space_before_catch_left_brace = true 412 | ij_groovy_space_before_catch_parentheses = true 413 | ij_groovy_space_before_class_left_brace = true 414 | ij_groovy_space_before_closure_left_brace = true 415 | ij_groovy_space_before_colon = true 416 | ij_groovy_space_before_comma = false 417 | ij_groovy_space_before_do_left_brace = true 418 | ij_groovy_space_before_else_keyword = true 419 | ij_groovy_space_before_else_left_brace = true 420 | ij_groovy_space_before_finally_keyword = true 421 | ij_groovy_space_before_finally_left_brace = true 422 | ij_groovy_space_before_for_left_brace = true 423 | ij_groovy_space_before_for_parentheses = true 424 | ij_groovy_space_before_for_semicolon = false 425 | ij_groovy_space_before_if_left_brace = true 426 | ij_groovy_space_before_if_parentheses = true 427 | ij_groovy_space_before_method_call_parentheses = false 428 | ij_groovy_space_before_method_left_brace = true 429 | ij_groovy_space_before_method_parentheses = false 430 | ij_groovy_space_before_quest = true 431 | ij_groovy_space_before_switch_left_brace = true 432 | ij_groovy_space_before_switch_parentheses = true 433 | ij_groovy_space_before_synchronized_left_brace = true 434 | ij_groovy_space_before_synchronized_parentheses = true 435 | ij_groovy_space_before_try_left_brace = true 436 | ij_groovy_space_before_try_parentheses = true 437 | ij_groovy_space_before_while_keyword = true 438 | ij_groovy_space_before_while_left_brace = true 439 | ij_groovy_space_before_while_parentheses = true 440 | ij_groovy_space_in_named_argument = true 441 | ij_groovy_space_in_named_argument_before_colon = false 442 | ij_groovy_space_within_empty_array_initializer_braces = false 443 | ij_groovy_space_within_empty_method_call_parentheses = false 444 | ij_groovy_spaces_around_additive_operators = true 445 | ij_groovy_spaces_around_assignment_operators = true 446 | ij_groovy_spaces_around_bitwise_operators = true 447 | ij_groovy_spaces_around_equality_operators = true 448 | ij_groovy_spaces_around_lambda_arrow = true 449 | ij_groovy_spaces_around_logical_operators = true 450 | ij_groovy_spaces_around_multiplicative_operators = true 451 | ij_groovy_spaces_around_regex_operators = true 452 | ij_groovy_spaces_around_relational_operators = true 453 | ij_groovy_spaces_around_shift_operators = true 454 | ij_groovy_spaces_within_annotation_parentheses = false 455 | ij_groovy_spaces_within_array_initializer_braces = false 456 | ij_groovy_spaces_within_braces = true 457 | ij_groovy_spaces_within_brackets = false 458 | ij_groovy_spaces_within_cast_parentheses = false 459 | ij_groovy_spaces_within_catch_parentheses = false 460 | ij_groovy_spaces_within_for_parentheses = false 461 | ij_groovy_spaces_within_gstring_injection_braces = false 462 | ij_groovy_spaces_within_if_parentheses = false 463 | ij_groovy_spaces_within_list_or_map = false 464 | ij_groovy_spaces_within_method_call_parentheses = false 465 | ij_groovy_spaces_within_method_parentheses = false 466 | ij_groovy_spaces_within_parentheses = false 467 | ij_groovy_spaces_within_switch_parentheses = false 468 | ij_groovy_spaces_within_synchronized_parentheses = false 469 | ij_groovy_spaces_within_try_parentheses = false 470 | ij_groovy_spaces_within_tuple_expression = false 471 | ij_groovy_spaces_within_while_parentheses = false 472 | ij_groovy_special_else_if_treatment = true 473 | ij_groovy_ternary_operation_wrap = normal 474 | ij_groovy_throws_keyword_wrap = normal 475 | ij_groovy_throws_list_wrap = normal 476 | ij_groovy_use_flying_geese_braces = false 477 | ij_groovy_use_fq_class_names = false 478 | ij_groovy_use_fq_class_names_in_javadoc = true 479 | ij_groovy_use_relative_indents = false 480 | ij_groovy_use_single_class_imports = true 481 | ij_groovy_variable_annotation_wrap = normal 482 | ij_groovy_while_brace_force = if_multiline 483 | ij_groovy_while_on_new_line = false 484 | ij_groovy_wrap_long_lines = false 485 | 486 | [{*.gradle.kts,*.kt,*.kts,*.main.kts}] 487 | ij_continuation_indent_size = 2 488 | ij_kotlin_align_in_columns_case_branch = false 489 | ij_kotlin_align_multiline_binary_operation = false 490 | ij_kotlin_align_multiline_extends_list = false 491 | ij_kotlin_align_multiline_method_parentheses = false 492 | ij_kotlin_align_multiline_parameters = false 493 | ij_kotlin_align_multiline_parameters_in_calls = false 494 | ij_kotlin_allow_trailing_comma = false 495 | ij_kotlin_allow_trailing_comma_on_call_site = false 496 | ij_kotlin_assignment_wrap = normal 497 | ij_kotlin_blank_lines_after_class_header = 0 498 | ij_kotlin_blank_lines_around_block_when_branches = 0 499 | ij_kotlin_blank_lines_before_declaration_with_comment_or_annotation_on_separate_line = 1 500 | ij_kotlin_block_comment_at_first_column = true 501 | ij_kotlin_call_parameters_new_line_after_left_paren = true 502 | ij_kotlin_call_parameters_right_paren_on_new_line = true 503 | ij_kotlin_call_parameters_wrap = normal 504 | ij_kotlin_catch_on_new_line = false 505 | ij_kotlin_class_annotation_wrap = split_into_lines 506 | ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL 507 | ij_kotlin_continuation_indent_for_chained_calls = true 508 | ij_kotlin_continuation_indent_for_expression_bodies = false 509 | ij_kotlin_continuation_indent_in_argument_lists = true 510 | ij_kotlin_continuation_indent_in_elvis = true 511 | ij_kotlin_continuation_indent_in_if_conditions = true 512 | ij_kotlin_continuation_indent_in_parameter_lists = false 513 | ij_kotlin_continuation_indent_in_supertype_lists = true 514 | ij_kotlin_else_on_new_line = false 515 | ij_kotlin_enum_constants_wrap = split_into_lines 516 | ij_kotlin_extends_list_wrap = on_every_item 517 | ij_kotlin_field_annotation_wrap = normal 518 | ij_kotlin_finally_on_new_line = false 519 | ij_kotlin_if_rparen_on_new_line = true 520 | ij_kotlin_import_nested_classes = true 521 | ij_kotlin_imports_layout = *,java.**,javax.**,kotlin.**,^ 522 | ij_kotlin_insert_whitespaces_in_simple_one_line_method = true 523 | ij_kotlin_keep_blank_lines_before_right_brace = 0 524 | ij_kotlin_keep_blank_lines_in_code = 1 525 | ij_kotlin_keep_blank_lines_in_declarations = 1 526 | ij_kotlin_keep_first_column_comment = true 527 | ij_kotlin_keep_indents_on_empty_lines = false 528 | ij_kotlin_keep_line_breaks = true 529 | ij_kotlin_lbrace_on_next_line = false 530 | ij_kotlin_line_comment_add_space = true 531 | ij_kotlin_line_comment_at_first_column = false 532 | ij_kotlin_method_annotation_wrap = normal 533 | ij_kotlin_method_call_chain_wrap = on_every_item 534 | ij_kotlin_method_parameters_new_line_after_left_paren = true 535 | ij_kotlin_method_parameters_right_paren_on_new_line = true 536 | ij_kotlin_method_parameters_wrap = split_into_lines 537 | ij_kotlin_name_count_to_use_star_import = 999 538 | ij_kotlin_name_count_to_use_star_import_for_members = 999 539 | ij_kotlin_parameter_annotation_wrap = off 540 | ij_kotlin_space_after_comma = true 541 | ij_kotlin_space_after_extend_colon = true 542 | ij_kotlin_space_after_type_colon = true 543 | ij_kotlin_space_before_catch_parentheses = true 544 | ij_kotlin_space_before_comma = false 545 | ij_kotlin_space_before_extend_colon = true 546 | ij_kotlin_space_before_for_parentheses = true 547 | ij_kotlin_space_before_if_parentheses = true 548 | ij_kotlin_space_before_lambda_arrow = true 549 | ij_kotlin_space_before_type_colon = false 550 | ij_kotlin_space_before_when_parentheses = true 551 | ij_kotlin_space_before_while_parentheses = true 552 | ij_kotlin_spaces_around_additive_operators = true 553 | ij_kotlin_spaces_around_assignment_operators = true 554 | ij_kotlin_spaces_around_equality_operators = true 555 | ij_kotlin_spaces_around_function_type_arrow = true 556 | ij_kotlin_spaces_around_logical_operators = true 557 | ij_kotlin_spaces_around_multiplicative_operators = true 558 | ij_kotlin_spaces_around_range = false 559 | ij_kotlin_spaces_around_relational_operators = true 560 | ij_kotlin_spaces_around_unary_operator = false 561 | ij_kotlin_spaces_around_when_arrow = true 562 | ij_kotlin_variable_annotation_wrap = off 563 | ij_kotlin_while_on_new_line = false 564 | ij_kotlin_wrap_elvis_expressions = 1 565 | ij_kotlin_wrap_expression_body_functions = 1 566 | ij_kotlin_wrap_first_method_in_call_chain = false 567 | 568 | [{*.har,*.json}] 569 | tab_width = 4 570 | ij_continuation_indent_size = 8 571 | ij_json_keep_blank_lines_in_code = 1 572 | ij_json_keep_indents_on_empty_lines = false 573 | ij_json_keep_line_breaks = true 574 | ij_json_space_after_colon = true 575 | ij_json_space_after_comma = true 576 | ij_json_space_before_colon = true 577 | ij_json_space_before_comma = false 578 | ij_json_spaces_within_braces = false 579 | ij_json_spaces_within_brackets = false 580 | ij_json_wrap_long_lines = false 581 | 582 | [{*.htm,*.html,*.sht,*.shtm,*.shtml}] 583 | indent_size = 4 584 | tab_width = 4 585 | ij_continuation_indent_size = 8 586 | ij_html_add_new_line_before_tags = body,div,p,form,h1,h2,h3 587 | ij_html_align_attributes = true 588 | ij_html_align_text = false 589 | ij_html_attribute_wrap = normal 590 | ij_html_block_comment_at_first_column = true 591 | ij_html_do_not_align_children_of_min_lines = 0 592 | ij_html_do_not_break_if_inline_tags = title,h1,h2,h3,h4,h5,h6,p 593 | ij_html_do_not_indent_children_of_tags = html,body,thead,tbody,tfoot 594 | ij_html_enforce_quotes = false 595 | ij_html_inline_tags = a,abbr,acronym,b,basefont,bdo,big,br,cite,cite,code,dfn,em,font,i,img,input,kbd,label,q,s,samp,select,small,span,strike,strong,sub,sup,textarea,tt,u,var 596 | ij_html_keep_blank_lines = 2 597 | ij_html_keep_indents_on_empty_lines = false 598 | ij_html_keep_line_breaks = true 599 | ij_html_keep_line_breaks_in_text = true 600 | ij_html_keep_whitespaces = false 601 | ij_html_keep_whitespaces_inside = span,pre,textarea 602 | ij_html_line_comment_at_first_column = true 603 | ij_html_new_line_after_last_attribute = never 604 | ij_html_new_line_before_first_attribute = never 605 | ij_html_quote_style = double 606 | ij_html_remove_new_line_before_tags = br 607 | ij_html_space_after_tag_name = false 608 | ij_html_space_around_equality_in_attribute = false 609 | ij_html_space_inside_empty_tag = false 610 | ij_html_text_wrap = normal 611 | ij_html_uniform_ident = false 612 | 613 | [{*.yaml,*.yml}] 614 | ij_yaml_keep_indents_on_empty_lines = false 615 | ij_yaml_keep_line_breaks = true 616 | ij_yaml_space_before_colon = true 617 | ij_yaml_spaces_within_braces = true 618 | ij_yaml_spaces_within_brackets = true -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # These owners will be the default owners for everything in 2 | # the repo, unless a later match takes precedence. 3 | * @pyricau 4 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | Open Source Code of Conduct 2 | =========================== 3 | 4 | At Square, we are committed to contributing to the open source community and simplifying the process 5 | of releasing and managing open source software. We’ve seen incredible support and enthusiasm from 6 | thousands of people who have already contributed to our projects — and we want to ensure ourcommunity 7 | continues to be truly open for everyone. 8 | 9 | This code of conduct outlines our expectations for participants, as well as steps to reporting 10 | unacceptable behavior. We are committed to providing a welcoming and inspiring community for all and 11 | expect our code of conduct to be honored. 12 | 13 | Square’s open source community strives to: 14 | 15 | * **Be open**: We invite anyone to participate in any aspect of our projects. Our community is 16 | open, and any responsibility can be carried by a contributor who demonstrates the required 17 | capacity and competence. 18 | 19 | * **Be considerate**: People use our work, and we depend on the work of others. Consider users and 20 | colleagues before taking action. For example, changes to code, infrastructure, policy, and 21 | documentation may negatively impact others. 22 | 23 | * **Be respectful**: We expect people to work together to resolve conflict, assume good intentions, 24 | and act with empathy. Do not turn disagreements into personal attacks. 25 | 26 | * **Be collaborative**: Collaboration reduces redundancy and improves the quality of our work. We 27 | strive for transparency within our open source community, and we work closely with upstream 28 | developers and others in the free software community to coordinate our efforts. 29 | 30 | * **Be pragmatic**: Questions are encouraged and should be asked early in the process to avoid 31 | problems later. Be thoughtful and considerate when seeking out the appropriate forum for your 32 | questions. Those who are asked should be responsive and helpful. 33 | 34 | * **Step down considerately**: Members of every project come and go. When somebody leaves or 35 | disengages from the project, they should make it known and take the proper steps to ensure that 36 | others can pick up where they left off. 37 | 38 | This code is not exhaustive or complete. It serves to distill our common understanding of a 39 | collaborative, shared environment, and goals. We expect it to be followed in spirit as much as in 40 | the letter. 41 | 42 | Diversity Statement 43 | ------------------- 44 | 45 | We encourage everyone to participate and are committed to building a community for all. Although we 46 | may not be able to satisfy everyone, we all agree that everyone is equal. 47 | 48 | Whenever a participant has made a mistake, we expect them to take responsibility for it. If someone 49 | has been harmed or offended, it is our responsibility to listen carefully and respectfully, and do 50 | our best to right the wrong. 51 | 52 | Although this list cannot be exhaustive, we explicitly honor diversity in age, culture, ethnicity, 53 | gender identity or expression, language, national origin, political beliefs, profession, race, 54 | religion, sexual orientation, socioeconomic status, and technical ability. We will not tolerate 55 | discrimination based on any of the protected characteristics above, including participants with 56 | disabilities. 57 | 58 | Reporting Issues 59 | ---------------- 60 | 61 | If you experience or witness unacceptable behavior — or have any other concerns — please report it by 62 | emailing [codeofconduct@squareup.com][codeofconduct_at]. For more details, please see our Reporting 63 | Guidelines below. 64 | 65 | Thanks 66 | ------ 67 | 68 | Some of the ideas and wording for the statements and guidelines above were based on work by the 69 | [Twitter][twitter_coc], [Ubuntu][ubuntu_coc], [GDC][gdc_coc], and [Django][django_coc] communities. 70 | We are thankful for their work. 71 | 72 | Reporting Guide 73 | --------------- 74 | 75 | If you experience or witness unacceptable behavior — or have any other concerns — please report it by 76 | emailing [codeofconduct@squareup.com][codeofconduct_at]. All reports will be handled with 77 | discretion. 78 | 79 | In your report please include: 80 | 81 | * Your contact information. 82 | * Names (real, nicknames, or pseudonyms) of any individuals involved. If there are additional 83 | witnesses, please include them as well. 84 | * Your account of what occurred, and if you believe the incident is ongoing. If there is a publicly 85 | available record (e.g. a mailing list archive or a public IRC logger), please include a link. 86 | * Any additional information that may be helpful. 87 | 88 | After filing a report, a representative from the Square Code of Conduct committee will contact you 89 | personally. The committee will then review the incident, follow up with any additional questions, 90 | and make a decision as to how to respond. 91 | 92 | Anyone asked to stop unacceptable behavior is expected to comply immediately. If an individual 93 | engages in unacceptable behavior, the Square Code of Conduct committee may take any action they deem 94 | appropriate, up to and including a permanent ban from all of Square spaces without warning. 95 | 96 | [codeofconduct_at]: mailto:codeofconduct@squareup.com 97 | [twitter_coc]: https://github.com/twitter/code-of-conduct/blob/master/code-of-conduct.md 98 | [ubuntu_coc]: https://ubuntu.com/community/code-of-conduct 99 | [gdc_coc]: https://www.gdconf.com/code-of-conduct 100 | [django_coc]: https://www.djangoproject.com/conduct/reporting/ 101 | 102 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributing 2 | ============ 3 | 4 | Contribute code by forking the repository on GitHub and sending a pull request. When submitting code, please make every effort to follow existing conventions and style in order to keep the code as readable as possible. -------------------------------------------------------------------------------- /.github/workflows/android.yml: -------------------------------------------------------------------------------- 1 | name: Android CI 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | # Build on all pull requests, regardless of target. 7 | pull_request: 8 | 9 | jobs: 10 | build: 11 | strategy: 12 | fail-fast: false 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v3 16 | - name: set up JDK 17 17 | uses: actions/setup-java@v3 18 | with: 19 | distribution: 'zulu' 20 | java-version: 17 21 | - name: Build with Gradle 22 | run: ./gradlew build 23 | -------------------------------------------------------------------------------- /.github/workflows/publish-release.yml: -------------------------------------------------------------------------------- 1 | name: Publish Release 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | publish-release: 8 | runs-on: ubuntu-latest 9 | if: github.repository == 'square/logcat' 10 | steps: 11 | - uses: actions/checkout@v3 12 | - name: set up JDK 17 13 | uses: actions/setup-java@v3 14 | with: 15 | distribution: 'zulu' 16 | java-version: 17 17 | - name: Build with Gradle 18 | run: ./gradlew build --stacktrace 19 | - name: Publish to Sonatype 20 | run: ./gradlew publish --no-daemon --no-parallel 21 | env: 22 | ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.SONATYPE_NEXUS_USERNAME }} 23 | ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.SONATYPE_NEXUS_PASSWORD }} 24 | ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.ARTIFACT_SIGNING_PRIVATE_KEY }} 25 | - name: Release to Maven Central 26 | run: ./gradlew closeAndReleaseRepository 27 | env: 28 | ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.SONATYPE_NEXUS_USERNAME }} 29 | ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.SONATYPE_NEXUS_PASSWORD }} 30 | ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.ARTIFACT_SIGNING_PRIVATE_KEY }} 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | 3 | # macOS 4 | .DS_Store 5 | 6 | # Compiled class file 7 | *.class 8 | 9 | # Log file 10 | *.log 11 | 12 | # BlueJ files 13 | *.ctxt 14 | 15 | # Mobile Tools for Java (J2ME) 16 | .mtj.tmp/ 17 | 18 | # Package Files # 19 | *.war 20 | *.nar 21 | *.ear 22 | *.zip 23 | *.tar.gz 24 | *.rar 25 | 26 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 27 | hs_err_pid* 28 | 29 | # Gradle 30 | out/ 31 | .gradle/ 32 | build/ 33 | local.properties 34 | .gradletasknamecache 35 | 36 | 37 | # Intellij 38 | *.iml 39 | .idea/ 40 | captures/ 41 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Change Log 2 | ========== 3 | 4 | Version 0.2.1 5 | ------------- 6 | 7 | _2025-05-19_ 8 | 9 | * Multiple `LogcatLogger` instances can now be added to `LoggatLogger.loggers`. 10 | * Breaking change: `LogcatLogger.install()` does not take a `LogcatLogger` instance 11 | anymore and instead takes an optional Executor to control the thread on which messages 12 | are evaluated. 13 | * Android libraries that want to use `logcat` should continue to use 14 | `AndroidLogcatLogger.installOnDebuggableApp()` which checks if already installed so as not to 15 | override any app specific set up. 16 | * Changed the `minPriority` default parameter value of 17 | `AndroidLogcatLogger.installOnDebuggableApp()` from `DEBUG` to `VERBOSE`. 18 | * Added `@JvmStatic` and removed `Kt` from class name (`LogcatKt` => `Logcat`) so that APIs are more Java friendly (this remains primarily a Kotlin focused APIs but this change helps with codebases that still have some Java code hanging around). 19 | 20 | 21 | Version 0.1 22 | ------------- 23 | 24 | _2021-09-21_ 25 | 26 | Initial alpha release. 27 | 28 | * Everything is new! 29 | 30 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Square Logcat 2 | 3 | ```kotlin 4 | logcat { "I CAN HAZ LOGZ?" } 5 | ``` 6 | 7 | A tiny Kotlin API for cheap logging on top of Android's normal `Log` class. 8 | 9 | ## Table of contents 10 | 11 | * [Setup](#setup) 12 | * [Usage](#usage) 13 | * [Motivations](#motivations) 14 | * [License](#license) 15 | 16 |  17 | 18 | _This fantastic logo is brought to you by [@rjrjr](https://github.com/rjrjr)._ 19 | 20 | ## Setup 21 | 22 | Add the `logcat` dependency to your library or app's `build.gradle` file: 23 | 24 | ```gradle 25 | dependencies { 26 | implementation 'com.squareup.logcat:logcat:0.2.1' 27 | } 28 | ``` 29 | 30 | Install `AndroidLogcatLogger` in `Application.onCreate()`: 31 | 32 | ```kotlin 33 | import android.app.Application 34 | import logcat.AndroidLogcatLogger 35 | import logcat.LogPriority.VERBOSE 36 | 37 | class ExampleApplication : Application() { 38 | override fun onCreate() { 39 | super.onCreate() 40 | // Log all priorities in debug builds, no-op in release builds. 41 | AndroidLogcatLogger.installOnDebuggableApp(this, minPriority = VERBOSE) 42 | } 43 | } 44 | ``` 45 | 46 | ## Usage 47 | 48 | The `logcat()` function has 3 parameters: an optional priority, an optional tag, and a required 49 | string producing lambda. The lambda is only evaluated if a logger is installed and the logger deems 50 | the priority loggable. 51 | 52 | The priority defaults to `LogPriority.DEBUG`. 53 | 54 | The tag defaults to the class name of the log call site, without any extra runtime cost. This works 55 | because `logcat()` is an inlined extension function of `Any` and has access to `this` from which 56 | it can extract the class name. If logging from a standalone function which has no `this`, use the 57 | `logcat` overload which requires a tag parameter. 58 | 59 | The `logcat()` function does not take a `Throwable` parameter. Instead, the library provides 60 | a Throwable extension function: `Throwable.asLog()` which returns a loggable string. 61 | 62 | ```kotlin 63 | import logcat.LogPriority.INFO 64 | import logcat.asLog 65 | import logcat.logcat 66 | 67 | class MouseController { 68 | 69 | fun play() { 70 | val state = "CHEEZBURGER" 71 | logcat { "I CAN HAZ $state?" } 72 | // logcat output: D/MouseController: I CAN HAZ CHEEZBURGER? 73 | 74 | logcat(INFO) { "DID U ASK 4 MOAR INFO?" } 75 | // logcat output: I/MouseController: DID U ASK 4 MOAR INFO? 76 | 77 | logcat { exception.asLog() } 78 | // logcat output: D/MouseController: java.lang.RuntimeException: FYLEZ KERUPTED 79 | // at sample.MouseController.play(MouseController.kt:22) 80 | // ... 81 | 82 | logcat("Lolcat") { "OH HI" } 83 | // logcat output: D/Lolcat: OH HI 84 | } 85 | } 86 | ``` 87 | 88 | ## Motivations 89 | 90 | We built this small library to fit the specific needs of the Square 91 | [Point of Sale](https://squareup.com/us/en/point-of-sale) application. We used 92 | [Timber](https://github.com/JakeWharton/timber) heavily before that, and love the simplicity of its 93 | API and the ability of its `DebugTree` to automatically figure out from which class it's being 94 | called from and use that class name as its tag. Here are our motivations for replacing it with 95 | `logcat()` in the Square Point of Sale: 96 | 97 | - Kotlin support for string interpolation is really nice. We love to use that for logs! 98 | Unfortunately that can be costly and a waste of good CPU if logging is disabled anyway. By using 99 | an inlined string producing lambda, `logcat()` supports string interpolation without any 100 | performance cost when logging is disabled. 101 | - Timber's `DebugTree` captures the calling class name as a tag by creating a stacktrace, which can 102 | be expensive. By making `logcat()` an extension function of `Any`, we can call `this::class.java` 103 | and get the calling context without creating a stacktrace. 104 | - The current implementation uses the outer most simple class name as the tag, i.e. the string 105 | between on the last `.` and the first `$`. That might not always be what you want. Also, when 106 | logging from an abstract class the tag will be the name of the subclass at runtime. We've found 107 | these limitations to be totally fine with us so far. 108 | - Most of the time, our developers just want to "send something to logcat" without even thinking 109 | about priority. `logcat()` picks "debug" as the right default to provide more consistency across 110 | a large codebase. Making the priority a parameter also means only one method to learn, and you 111 | don't have to learn / think about priorities prior to writing a log. This becomes especially 112 | important when there are several parameters requiring overloads (e.g. in Timber (6 priorities + 1 113 | generic log method) * 3 overloads = 21 methods to choose from). 114 | - The lack of throwable parameter is also intentional. It just creates more overloads and confusion 115 | (e.g. "what's the param order?"), when really logs are about strings and all you need is an easy 116 | way to turn a throwable into a loggable string. Hence `Throwable.asLog()`. 117 | - The name `logcat()` is intentionally boring and identical to the Android command line tool. This 118 | makes it easy to remember and developers know exactly what this does, i.e. log to the local device. 119 | One could setup a custom logger that send logs remotely in release builds, however we do not 120 | recommend doing so: in our experience, remote logs should be distinct in code from local logs and 121 | clearly identified as such, because the volume and performance impact should be very distinct. 122 | - The API for installing a logger is separated out from the API to log, as these operations occur 123 | in very distinct contexts. 124 | 125 | ## License 126 | 127 |
128 | Copyright 2021 Square Inc. 129 | 130 | Licensed under the Apache License, Version 2.0 (the "License"); 131 | you may not use this file except in compliance with the License. 132 | You may obtain a copy of the License at 133 | 134 | http://www.apache.org/licenses/LICENSE-2.0 135 | 136 | Unless required by applicable law or agreed to in writing, software 137 | distributed under the License is distributed on an "AS IS" BASIS, 138 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 139 | See the License for the specific language governing permissions and 140 | limitations under the License. 141 |142 | -------------------------------------------------------------------------------- /RELEASING.md: -------------------------------------------------------------------------------- 1 | # Releasing 2 | 3 | 4 | ## Set up GitHub CLI 5 | 6 | Install GitHub CLI 7 | 8 | ```bash 9 | brew install gh 10 | ``` 11 | 12 | ## Creating the release 13 | 14 | * Update the changelog 15 | ```bash 16 | mate CHANGELOG.md 17 | ``` 18 | 19 | * Create a local release branch from `main` and update `VERSION_NAME` in `gradle.properties` (removing `-SNAPSHOT`) and the README, then run the publish workflow and finish the release: 20 | 21 | ```bash 22 | git checkout main && \ 23 | git pull && \ 24 | git checkout -b release_{NEW_VERSION} && \ 25 | sed -i '' 's/VERSION_NAME=.*-SNAPSHOT/VERSION_NAME={NEW_VERSION}/' gradle.properties 26 | sed -i '' "s/com.squareup.logcat:logcat:.*'/com.squareup.logcat:logcat:{NEW_VERSION}'/" README.md && \ 27 | git commit -am "Prepare {NEW_VERSION} release" && \ 28 | git tag v{NEW_VERSION} && \ 29 | git push origin v{NEW_VERSION} && \ 30 | gh workflow run publish-release.yml --ref v{NEW_VERSION} && \ 31 | sleep 5 &&\ 32 | gh run list --workflow=publish-release.yml --branch v{NEW_VERSION} --json databaseId --jq ".[].databaseId" | xargs -I{} gh run watch {} --exit-status && \ 33 | git checkout main && \ 34 | git pull && \ 35 | git merge --no-ff --no-edit release_{NEW_VERSION} && \ 36 | sed -i '' 's/VERSION_NAME={NEW_VERSION}/VERSION_NAME={NEXT_VERSION}-SNAPSHOT/' gradle.properties && \ 37 | git commit -am "Prepare for next development iteration" && \ 38 | git push && \ 39 | gh release create v{NEW_VERSION} --title v{NEW_VERSION} --notes 'See [Change Log](https://github.com/square/logcat/blob/main/CHANGELOG.md)' 40 | ``` 41 | 42 | * Wait for the release to be available [on Maven Central](https://repo1.maven.org/maven2/com/squareup/logcat/logcat/). 43 | * Tell your friends, update all of your apps, and tweet the new release. As a nice extra touch, mention external contributions. -------------------------------------------------------------------------------- /assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/square/logcat/3a08fd5725b91a2a3920dd27920b74faab0be812/assets/logo.png -------------------------------------------------------------------------------- /assets/logo_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/square/logcat/3a08fd5725b91a2a3920dd27920b74faab0be812/assets/logo_512.png -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | import com.vanniktech.maven.publish.AndroidSingleVariantLibrary 2 | import com.vanniktech.maven.publish.MavenPublishBaseExtension 3 | import com.vanniktech.maven.publish.SonatypeHost 4 | import kotlinx.validation.ApiValidationExtension 5 | import org.jetbrains.kotlin.gradle.tasks.KotlinCompile 6 | import org.jlleitschuh.gradle.ktlint.KtlintExtension 7 | import org.jlleitschuh.gradle.ktlint.reporter.ReporterType 8 | 9 | println("Building with Kotlin compiler version ${Versions.KotlinCompiler}") 10 | 11 | buildscript { 12 | repositories { 13 | mavenCentral() 14 | gradlePluginPortal() 15 | google() 16 | } 17 | 18 | dependencies { 19 | classpath(Dependencies.Build.Android) 20 | classpath(Dependencies.Build.MavenPublish) 21 | classpath(Dependencies.Build.Kotlin) 22 | classpath(Dependencies.Build.Ktlint) 23 | classpath(Dependencies.Build.BinaryCompatibility) 24 | // Required for the gradle-maven-publish-plugin plugin. 25 | // See https://github.com/vanniktech/gradle-maven-publish-plugin/issues/205. 26 | classpath(Dependencies.Build.Dokka) 27 | } 28 | } 29 | 30 | // We use JetBrain's Kotlin Binary Compatibility Validator to track changes to our public binary 31 | // APIs. 32 | // When making a change that results in a public ABI change, the apiCheck task will fail. When this 33 | // happens, run ./gradlew apiDump to generate updated *.api files, and add those to your commit. 34 | // See https://github.com/Kotlin/binary-compatibility-validator 35 | apply(plugin = "binary-compatibility-validator") 36 | 37 | extensions.configure